diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index 9fce1762c6ea7..13d03562cce49 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -996,11 +996,11 @@ impl< } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> O { + fn try_successful_origin() -> Result<O, ()> { let zero_account_id = AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()) .expect("infinite length input; no invalid inputs for type; qed"); - O::from(RawOrigin::Member(zero_account_id)) + Ok(O::from(RawOrigin::Member(zero_account_id))) } } @@ -1021,8 +1021,8 @@ impl< } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> O { - O::from(RawOrigin::Members(N, N)) + fn try_successful_origin() -> Result<O, ()> { + Ok(O::from(RawOrigin::Members(N, N))) } } @@ -1046,8 +1046,8 @@ impl< } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> O { - O::from(RawOrigin::Members(1u32, 0u32)) + fn try_successful_origin() -> Result<O, ()> { + Ok(O::from(RawOrigin::Members(1u32, 0u32))) } } @@ -1071,7 +1071,7 @@ impl< } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> O { - O::from(RawOrigin::Members(0u32, 0u32)) + fn try_successful_origin() -> Result<O, ()> { + Ok(O::from(RawOrigin::Members(0u32, 0u32))) } } diff --git a/frame/society/src/lib.rs b/frame/society/src/lib.rs index 5a993f72f32d2..2a6428e754b9d 100644 --- a/frame/society/src/lib.rs +++ b/frame/society/src/lib.rs @@ -1272,9 +1272,9 @@ impl<T: Config> EnsureOrigin<T::Origin> for EnsureFounder<T> { } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> T::Origin { - let founder = Founder::<T>::get().expect("society founder should exist"); - T::Origin::from(frame_system::RawOrigin::Signed(founder)) + fn try_successful_origin() -> Result<T::Origin, ()> { + let founder = Founder::<T>::get().ok_or(())?; + Ok(T::Origin::from(frame_system::RawOrigin::Signed(founder))) } } diff --git a/frame/support/src/traits/dispatch.rs b/frame/support/src/traits/dispatch.rs index b1bd52ca960da..e06373348e499 100644 --- a/frame/support/src/traits/dispatch.rs +++ b/frame/support/src/traits/dispatch.rs @@ -18,10 +18,14 @@ //! Traits for dealing with dispatching calls and the origin from which they are dispatched. use crate::dispatch::{DispatchResultWithPostInfo, Parameter, RawOrigin}; +use sp_arithmetic::traits::{CheckedSub, Zero}; use sp_runtime::{ - traits::{BadOrigin, Member}, + traits::{BadOrigin, Member, Morph, TryMorph}, Either, }; +use sp_std::marker::PhantomData; + +use super::TypedGet; /// Some sort of check on the origin is performed by this object. pub trait EnsureOrigin<OuterOrigin> { @@ -38,9 +42,23 @@ pub trait EnsureOrigin<OuterOrigin> { /// Returns an outer origin capable of passing `try_origin` check. /// + /// NOTE: This should generally *NOT* be reimplemented. Instead implement + /// `try_successful_origin`. + /// /// ** Should be used for benchmarking only!!! ** #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> OuterOrigin; + fn successful_origin() -> OuterOrigin { + Self::try_successful_origin().expect("No origin exists which can satisfy the guard") + } + + /// Attept to get an outer origin capable of passing `try_origin` check. May return `Err` if it + /// is impossible. Default implementation just uses `successful_origin()`. + /// + /// ** Should be used for benchmarking only!!! ** + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result<OuterOrigin, ()> { + Ok(Self::successful_origin()) + } } /// `EnsureOrigin` implementation that always fails. @@ -51,8 +69,8 @@ impl<OO, Success> EnsureOrigin<OO> for NeverEnsureOrigin<Success> { Err(o) } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> OO { - panic!("No `successful_origin` possible for `NeverEnsureOrigin`") + fn try_successful_origin() -> Result<OO, ()> { + Err(()) } } @@ -71,9 +89,23 @@ pub trait EnsureOriginWithArg<OuterOrigin, Argument> { /// Returns an outer origin capable of passing `try_origin` check. /// + /// NOTE: This should generally *NOT* be reimplemented. Instead implement + /// `try_successful_origin`. + /// /// ** Should be used for benchmarking only!!! ** #[cfg(feature = "runtime-benchmarks")] - fn successful_origin(a: &Argument) -> OuterOrigin; + fn successful_origin(a: &Argument) -> OuterOrigin { + Self::try_successful_origin(a).expect("No origin exists which can satisfy the guard") + } + + /// Attept to get an outer origin capable of passing `try_origin` check. May return `Err` if it + /// is impossible. Default implementation just uses `successful_origin()`. + /// + /// ** Should be used for benchmarking only!!! ** + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(a: &Argument) -> Result<OuterOrigin, ()> { + Ok(Self::successful_origin(a)) + } } pub struct AsEnsureOriginWithArg<EO>(sp_std::marker::PhantomData<EO>); @@ -97,8 +129,121 @@ impl<OuterOrigin, Argument, EO: EnsureOrigin<OuterOrigin>> /// /// ** Should be used for benchmarking only!!! ** #[cfg(feature = "runtime-benchmarks")] - fn successful_origin(_: &Argument) -> OuterOrigin { - EO::successful_origin() + fn try_successful_origin(_: &Argument) -> Result<OuterOrigin, ()> { + EO::try_successful_origin() + } +} + +/// A derivative `EnsureOrigin` implementation. It mutates the `Success` result of an `Original` +/// implementation with a given `Mutator`. +pub struct MapSuccess<Original, Mutator>(PhantomData<(Original, Mutator)>); +impl<O, Original: EnsureOrigin<O>, Mutator: Morph<Original::Success>> EnsureOrigin<O> + for MapSuccess<Original, Mutator> +{ + type Success = Mutator::Outcome; + fn try_origin(o: O) -> Result<Mutator::Outcome, O> { + Ok(Mutator::morph(Original::try_origin(o)?)) + } + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result<O, ()> { + Original::try_successful_origin() + } +} + +/// A derivative `EnsureOrigin` implementation. It mutates the `Success` result of an `Original` +/// implementation with a given `Mutator`, allowing the possibility of an error to be returned +/// from the mutator. +/// +/// NOTE: This is strictly worse performance than `MapSuccess` since it clones the original origin +/// value. If possible, use `MapSuccess` instead. +pub struct TryMapSuccess<Orig, Mutator>(PhantomData<(Orig, Mutator)>); +impl<O: Clone, Original: EnsureOrigin<O>, Mutator: TryMorph<Original::Success>> EnsureOrigin<O> + for TryMapSuccess<Original, Mutator> +{ + type Success = Mutator::Outcome; + fn try_origin(o: O) -> Result<Mutator::Outcome, O> { + let orig = o.clone(); + Mutator::try_morph(Original::try_origin(o)?).map_err(|()| orig) + } + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result<O, ()> { + Original::try_successful_origin() + } +} + +/// "OR gate" implementation of `EnsureOrigin` allowing for different `Success` types for `L` +/// and `R`, with them combined using an `Either` type. +/// +/// Origin check will pass if `L` or `R` origin check passes. `L` is tested first. +/// +/// Successful origin is derived from the left side. +pub struct EitherOfDiverse<L, R>(sp_std::marker::PhantomData<(L, R)>); +impl<OuterOrigin, L: EnsureOrigin<OuterOrigin>, R: EnsureOrigin<OuterOrigin>> + EnsureOrigin<OuterOrigin> for EitherOfDiverse<L, R> +{ + type Success = Either<L::Success, R::Success>; + fn try_origin(o: OuterOrigin) -> Result<Self::Success, OuterOrigin> { + L::try_origin(o) + .map_or_else(|o| R::try_origin(o).map(Either::Right), |o| Ok(Either::Left(o))) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result<OuterOrigin, ()> { + L::try_successful_origin().or_else(|()| R::try_successful_origin()) + } +} + +/// "OR gate" implementation of `EnsureOrigin` allowing for different `Success` types for `L` +/// and `R`, with them combined using an `Either` type. +/// +/// Origin check will pass if `L` or `R` origin check passes. `L` is tested first. +/// +/// Successful origin is derived from the left side. +#[deprecated = "Use `EitherOfDiverse` instead"] +pub type EnsureOneOf<L, R> = EitherOfDiverse<L, R>; + +/// "OR gate" implementation of `EnsureOrigin`, `Success` type for both `L` and `R` must +/// be equal. +/// +/// Origin check will pass if `L` or `R` origin check passes. `L` is tested first. +/// +/// Successful origin is derived from the left side. +pub struct EitherOf<L, R>(sp_std::marker::PhantomData<(L, R)>); +impl< + OuterOrigin, + L: EnsureOrigin<OuterOrigin>, + R: EnsureOrigin<OuterOrigin, Success = L::Success>, + > EnsureOrigin<OuterOrigin> for EitherOf<L, R> +{ + type Success = L::Success; + fn try_origin(o: OuterOrigin) -> Result<Self::Success, OuterOrigin> { + L::try_origin(o).or_else(|o| R::try_origin(o)) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result<OuterOrigin, ()> { + L::try_successful_origin().or_else(|()| R::try_successful_origin()) + } +} + +/// Mutator which reduces a scalar by a particular amount. +pub struct ReduceBy<N>(PhantomData<N>); +impl<N: TypedGet> TryMorph<N::Type> for ReduceBy<N> +where + N::Type: CheckedSub, +{ + type Outcome = N::Type; + fn try_morph(r: N::Type) -> Result<N::Type, ()> { + r.checked_sub(&N::get()).ok_or(()) + } +} +impl<N: TypedGet> Morph<N::Type> for ReduceBy<N> +where + N::Type: CheckedSub + Zero, +{ + type Outcome = N::Type; + fn morph(r: N::Type) -> N::Type { + r.checked_sub(&N::get()).unwrap_or(Zero::zero()) } } @@ -176,63 +321,6 @@ pub trait OriginTrait: Sized { fn signed(by: Self::AccountId) -> Self; } -/// "OR gate" implementation of `EnsureOrigin` allowing for different `Success` types for `L` -/// and `R`, with them combined using an `Either` type. -/// -/// Origin check will pass if `L` or `R` origin check passes. `L` is tested first. -/// -/// Successful origin is derived from the left side. -pub struct EitherOfDiverse<L, R>(sp_std::marker::PhantomData<(L, R)>); - -impl<OuterOrigin, L: EnsureOrigin<OuterOrigin>, R: EnsureOrigin<OuterOrigin>> - EnsureOrigin<OuterOrigin> for EitherOfDiverse<L, R> -{ - type Success = Either<L::Success, R::Success>; - fn try_origin(o: OuterOrigin) -> Result<Self::Success, OuterOrigin> { - L::try_origin(o) - .map_or_else(|o| R::try_origin(o).map(Either::Right), |o| Ok(Either::Left(o))) - } - - #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> OuterOrigin { - L::successful_origin() - } -} - -/// "OR gate" implementation of `EnsureOrigin` allowing for different `Success` types for `L` -/// and `R`, with them combined using an `Either` type. -/// -/// Origin check will pass if `L` or `R` origin check passes. `L` is tested first. -/// -/// Successful origin is derived from the left side. -#[deprecated = "Use `EitherOfDiverse` instead"] -pub type EnsureOneOf<L, R> = EitherOfDiverse<L, R>; - -/// "OR gate" implementation of `EnsureOrigin`, `Success` type for both `L` and `R` must -/// be equal. -/// -/// Origin check will pass if `L` or `R` origin check passes. `L` is tested first. -/// -/// Successful origin is derived from the left side. -pub struct EitherOf<L, R>(sp_std::marker::PhantomData<(L, R)>); - -impl< - OuterOrigin, - L: EnsureOrigin<OuterOrigin>, - R: EnsureOrigin<OuterOrigin, Success = L::Success>, - > EnsureOrigin<OuterOrigin> for EitherOf<L, R> -{ - type Success = L::Success; - fn try_origin(o: OuterOrigin) -> Result<Self::Success, OuterOrigin> { - L::try_origin(o).or_else(|o| R::try_origin(o)) - } - - #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> OuterOrigin { - L::successful_origin() - } -} - #[cfg(test)] mod tests { use super::*; @@ -248,8 +336,8 @@ mod tests { Ok(V::get()) } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> () { - () + fn try_successful_origin() -> Result<(), ()> { + Ok(()) } } @@ -259,8 +347,8 @@ mod tests { Err(()) } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> () { - () + fn try_successful_origin() -> Result<(), ()> { + Err(()) } } diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index bc28cc7e8118e..370a802665918 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -66,6 +66,8 @@ #[cfg(feature = "std")] use serde::Serialize; +#[cfg(feature = "runtime-benchmarks")] +use sp_runtime::traits::TrailingZeroInput; use sp_runtime::{ generic, traits::{ @@ -782,8 +784,8 @@ impl<O: Into<Result<RawOrigin<AccountId>, O>> + From<RawOrigin<AccountId>>, Acco } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> O { - O::from(RawOrigin::Root) + fn try_successful_origin() -> Result<O, ()> { + Ok(O::from(RawOrigin::Root)) } } @@ -805,8 +807,8 @@ impl< } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> O { - O::from(RawOrigin::Root) + fn try_successful_origin() -> Result<O, ()> { + Ok(O::from(RawOrigin::Root)) } } @@ -823,11 +825,10 @@ impl<O: Into<Result<RawOrigin<AccountId>, O>> + From<RawOrigin<AccountId>>, Acco } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> O { + fn try_successful_origin() -> Result<O, ()> { let zero_account_id = - AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()) - .expect("infinite length input; no invalid inputs for type; qed"); - O::from(RawOrigin::Signed(zero_account_id)) + AccountId::decode(&mut TrailingZeroInput::zeroes()).map_err(|_| ())?; + Ok(O::from(RawOrigin::Signed(zero_account_id))) } } @@ -847,16 +848,15 @@ impl< } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> O { + fn try_successful_origin() -> Result<O, ()> { let zero_account_id = - AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()) - .expect("infinite length input; no invalid inputs for type; qed"); + AccountId::decode(&mut TrailingZeroInput::zeroes()).map_err(|_| ())?; let members = Who::sorted_members(); let first_member = match members.get(0) { Some(account) => account.clone(), None => zero_account_id, }; - O::from(RawOrigin::Signed(first_member)) + Ok(O::from(RawOrigin::Signed(first_member))) } } @@ -873,8 +873,8 @@ impl<O: Into<Result<RawOrigin<AccountId>, O>> + From<RawOrigin<AccountId>>, Acco } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> O { - O::from(RawOrigin::None) + fn try_successful_origin() -> Result<O, ()> { + Ok(O::from(RawOrigin::None)) } } @@ -886,8 +886,8 @@ impl<O, T> EnsureOrigin<O> for EnsureNever<T> { } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> O { - unimplemented!() + fn try_successful_origin() -> Result<O, ()> { + Err(()) } } diff --git a/frame/treasury/src/tests.rs b/frame/treasury/src/tests.rs index a21296d1b39ec..61eafb652427b 100644 --- a/frame/treasury/src/tests.rs +++ b/frame/treasury/src/tests.rs @@ -116,8 +116,8 @@ impl frame_support::traits::EnsureOrigin<Origin> for TestSpendOrigin { }) } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> Origin { - Origin::root() + fn try_successful_origin() -> Result<Origin, ()> { + Ok(Origin::root()) } } diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index 1b21e7c65ddf7..ee6a526e95e1e 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -274,6 +274,42 @@ where } } +/// Extensible conversion trait. Generic over only source type, with destination type being +/// associated. +pub trait Morph<A> { + /// The type into which `A` is mutated. + type Outcome; + + /// Make conversion. + fn morph(a: A) -> Self::Outcome; +} + +/// A structure that performs identity conversion. +impl<T> Morph<T> for Identity { + type Outcome = T; + fn morph(a: T) -> T { + a + } +} + +/// Extensible conversion trait. Generic over only source type, with destination type being +/// associated. +pub trait TryMorph<A> { + /// The type into which `A` is mutated. + type Outcome; + + /// Make conversion. + fn try_morph(a: A) -> Result<Self::Outcome, ()>; +} + +/// A structure that performs identity conversion. +impl<T> TryMorph<T> for Identity { + type Outcome = T; + fn try_morph(a: T) -> Result<T, ()> { + Ok(a) + } +} + /// Extensible conversion trait. Generic over both source and destination types. pub trait Convert<A, B> { /// Make conversion.