// This file is part of Substrate. // Copyright (C) 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, Parameter, RawOrigin}; use codec::MaxEncodedLen; use core::{cmp::Ordering, marker::PhantomData}; use sp_runtime::{ traits::{BadOrigin, Get, Member, Morph, TryMorph}, Either, }; use super::misc; /// 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) } /// The same as `ensure_origin` except that Root origin will always pass. This can only be /// used if `Success` has a sensible impl of `Default` since that will be used in the result. fn ensure_origin_or_root(o: OuterOrigin) -> Result, BadOrigin> where OuterOrigin: OriginTrait, { if o.caller().is_root() { return Ok(None) } else { Self::ensure_origin(o).map(Some) } } /// Perform the origin check. fn try_origin(o: OuterOrigin) -> Result; /// The same as `try_origin` except that Root origin will always pass. This can only be /// used if `Success` has a sensible impl of `Default` since that will be used in the result. fn try_origin_or_root(o: OuterOrigin) -> Result, OuterOrigin> where OuterOrigin: OriginTrait, { if o.caller().is_root() { return Ok(None) } else { Self::try_origin(o).map(Some) } } /// Attempt to get an outer origin capable of passing `try_origin` check. May return `Err` if it /// is impossible. /// /// ** Should be used for benchmarking only!!! ** #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result; } /// [`EnsureOrigin`] implementation that checks that an origin has equal or higher privilege /// compared to the expected `Origin`. /// /// It will take the shortcut of comparing the incoming origin with the expected `Origin` and if /// both are the same the origin is accepted. /// /// # Example /// /// ```rust /// # use frame_support::traits::{EnsureOriginEqualOrHigherPrivilege, PrivilegeCmp, EnsureOrigin as _}; /// # use sp_runtime::traits::{parameter_types, Get}; /// # use core::cmp::Ordering; /// /// #[derive(Eq, PartialEq, Debug)] /// pub enum Origin { /// Root, /// SomethingBelowRoot, /// NormalUser, /// } /// /// struct OriginPrivilegeCmp; /// /// impl PrivilegeCmp for OriginPrivilegeCmp { /// fn cmp_privilege(left: &Origin, right: &Origin) -> Option { /// match (left, right) { /// (Origin::Root, Origin::Root) => Some(Ordering::Equal), /// (Origin::Root, _) => Some(Ordering::Greater), /// (Origin::SomethingBelowRoot, Origin::SomethingBelowRoot) => Some(Ordering::Equal), /// (Origin::SomethingBelowRoot, Origin::Root) => Some(Ordering::Less), /// (Origin::SomethingBelowRoot, Origin::NormalUser) => Some(Ordering::Greater), /// (Origin::NormalUser, Origin::NormalUser) => Some(Ordering::Equal), /// (Origin::NormalUser, _) => Some(Ordering::Less), /// } /// } /// } /// /// parameter_types! { /// pub const ExpectedOrigin: Origin = Origin::SomethingBelowRoot; /// } /// /// type EnsureOrigin = EnsureOriginEqualOrHigherPrivilege; /// /// // `Root` has an higher privilege as our expected origin. /// assert!(EnsureOrigin::ensure_origin(Origin::Root).is_ok()); /// // `SomethingBelowRoot` is exactly the expected origin. /// assert!(EnsureOrigin::ensure_origin(Origin::SomethingBelowRoot).is_ok()); /// // The `NormalUser` origin is not allowed. /// assert!(EnsureOrigin::ensure_origin(Origin::NormalUser).is_err()); /// ``` pub struct EnsureOriginEqualOrHigherPrivilege( core::marker::PhantomData<(Origin, PrivilegeCmp)>, ); impl EnsureOrigin for EnsureOriginEqualOrHigherPrivilege where Origin: Get, OuterOrigin: Eq, PrivilegeCmp: misc::PrivilegeCmp, { type Success = (); fn try_origin(o: OuterOrigin) -> Result { let expected_origin = Origin::get(); // If this is the expected origin, it has the same privilege. if o == expected_origin { return Ok(()) } let cmp = PrivilegeCmp::cmp_privilege(&o, &expected_origin); match cmp { Some(Ordering::Equal) | Some(Ordering::Greater) => Ok(()), None | Some(Ordering::Less) => Err(o), } } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result { Ok(Origin::get()) } } /// Some sort of check on the origin is performed by this object. pub trait EnsureOriginWithArg { /// A return type. type Success; /// Perform the origin check. fn ensure_origin(o: OuterOrigin, a: &Argument) -> Result { Self::try_origin(o, a).map_err(|_| BadOrigin) } /// Perform the origin check, returning the origin value if unsuccessful. This allows chaining. fn try_origin(o: OuterOrigin, a: &Argument) -> Result; /// Attempt to get an outer origin capable of passing `try_origin` check. May return `Err` if it /// is impossible. /// /// ** Should be used for benchmarking only!!! ** #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin(a: &Argument) -> Result; } /// Simple macro to explicitly implement [EnsureOriginWithArg] to be used on any type which /// implements [EnsureOrigin]. This is quick and dirty, so you must use the type parameters `O` /// (the origin type), `T` (the argument type) and `AccountId` (if you are using the `O: ..` form). /// /// The argument is ignored, much like in [AsEnsureOriginWithArg]. #[macro_export] macro_rules! impl_ensure_origin_with_arg_ignoring_arg { ( impl < { O: .., I: 'static, $( $bound:tt )* }> EnsureOriginWithArg for $name:ty {} ) => { impl_ensure_origin_with_arg_ignoring_arg! { impl <{ O: Into, O>> + From>, I: 'static, $( $bound )* }> EnsureOriginWithArg for $name {} } }; ( impl < { O: .. , $( $bound:tt )* }> EnsureOriginWithArg for $name:ty {} ) => { impl_ensure_origin_with_arg_ignoring_arg! { impl <{ O: Into, O>> + From>, $( $bound )* }> EnsureOriginWithArg for $name {} } }; ( impl < { $( $bound:tt )* } > EnsureOriginWithArg<$o_param:ty, $t_param:ty> for $name:ty {} ) => { impl < $( $bound )* > EnsureOriginWithArg<$o_param, $t_param> for $name { type Success = >::Success; fn try_origin(o: $o_param, _: &$t_param) -> Result { >::try_origin(o) } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin(_: &$t_param) -> Result<$o_param, ()> { >::try_successful_origin() } } } } /// [`EnsureOrigin`] implementation that always fails. pub struct NeverEnsureOrigin(core::marker::PhantomData); impl EnsureOrigin for NeverEnsureOrigin { type Success = Success; fn try_origin(o: OO) -> Result { Err(o) } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result { Err(()) } } impl_ensure_origin_with_arg_ignoring_arg! { impl<{ OO, Success, A }> EnsureOriginWithArg for NeverEnsureOrigin {} } pub struct AsEnsureOriginWithArg(core::marker::PhantomData); impl> EnsureOriginWithArg for AsEnsureOriginWithArg { /// A return type. type Success = EO::Success; /// Perform the origin check. fn ensure_origin(o: OuterOrigin, _: &Argument) -> Result { EO::ensure_origin(o) } /// Perform the origin check, returning the origin value if unsuccessful. This allows chaining. fn try_origin(o: OuterOrigin, _: &Argument) -> Result { EO::try_origin(o) } /// Returns an outer origin capable of passing `try_origin` check. /// /// ** Should be used for benchmarking only!!! ** #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin(_: &Argument) -> Result { EO::try_successful_origin() } } /// A derivative `EnsureOrigin` implementation. It mutates the `Success` result of an `Original` /// implementation with a given `Mutator`. pub struct MapSuccess(PhantomData<(Original, Mutator)>); impl, Mutator: Morph> EnsureOrigin for MapSuccess { type Success = Mutator::Outcome; fn try_origin(o: O) -> Result { Ok(Mutator::morph(Original::try_origin(o)?)) } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result { Original::try_successful_origin() } } impl, Mutator: Morph, A> EnsureOriginWithArg for MapSuccess { type Success = Mutator::Outcome; fn try_origin(o: O, a: &A) -> Result { Ok(Mutator::morph(Original::try_origin(o, a)?)) } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin(a: &A) -> Result { Original::try_successful_origin(a) } } /// 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(PhantomData<(Orig, Mutator)>); impl, Mutator: TryMorph> EnsureOrigin for TryMapSuccess { type Success = Mutator::Outcome; fn try_origin(o: O) -> Result { let orig = o.clone(); Mutator::try_morph(Original::try_origin(o)?).map_err(|()| orig) } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result { Original::try_successful_origin() } } impl, Mutator: TryMorph, A> EnsureOriginWithArg for TryMapSuccess { type Success = Mutator::Outcome; fn try_origin(o: O, a: &A) -> Result { let orig = o.clone(); Mutator::try_morph(Original::try_origin(o, a)?).map_err(|()| orig) } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin(a: &A) -> Result { Original::try_successful_origin(a) } } pub struct TryWithMorphedArg( PhantomData<(O, A, Morph, Inner, Success)>, ); impl< O, A, Morph: for<'a> TryMorph<&'a A>, Inner: for<'a> EnsureOriginWithArg>::Outcome, Success = Success>, Success, > EnsureOriginWithArg for TryWithMorphedArg { type Success = Success; fn try_origin(o: O, a: &A) -> Result { match Morph::try_morph(a) { Ok(x) => Inner::try_origin(o, &x), _ => return Err(o), } } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin(a: &A) -> Result { Inner::try_successful_origin(&Morph::try_morph(a).map_err(|_| ())?) } } /// "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(core::marker::PhantomData<(L, R)>); impl, R: EnsureOrigin> EnsureOrigin for EitherOfDiverse { type Success = Either; fn try_origin(o: OuterOrigin) -> Result { 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 { L::try_successful_origin().or_else(|()| R::try_successful_origin()) } } impl< OuterOrigin, L: EnsureOriginWithArg, R: EnsureOriginWithArg, Argument, > EnsureOriginWithArg for EitherOfDiverse { type Success = Either; fn try_origin(o: OuterOrigin, a: &Argument) -> Result { L::try_origin(o, a) .map_or_else(|o| R::try_origin(o, a).map(Either::Right), |o| Ok(Either::Left(o))) } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin(a: &Argument) -> Result { L::try_successful_origin(a).or_else(|()| R::try_successful_origin(a)) } } /// "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 = EitherOfDiverse; /// "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(core::marker::PhantomData<(L, R)>); impl< OuterOrigin, L: EnsureOrigin, R: EnsureOrigin, > EnsureOrigin for EitherOf { type Success = L::Success; fn try_origin(o: OuterOrigin) -> Result { L::try_origin(o).or_else(|o| R::try_origin(o)) } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result { L::try_successful_origin().or_else(|()| R::try_successful_origin()) } } impl< OuterOrigin, L: EnsureOriginWithArg, R: EnsureOriginWithArg, Argument, > EnsureOriginWithArg for EitherOf { type Success = L::Success; fn try_origin(o: OuterOrigin, a: &Argument) -> Result { L::try_origin(o, a).or_else(|o| R::try_origin(o, a)) } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin(a: &Argument) -> Result { L::try_successful_origin(a).or_else(|()| R::try_successful_origin(a)) } } /// 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`. pub trait UnfilteredDispatchable { /// The origin type of the runtime, (i.e. `frame_system::Config::RuntimeOrigin`). type RuntimeOrigin; /// Dispatch this call but do not check the filter in origin. fn dispatch_bypass_filter(self, origin: Self::RuntimeOrigin) -> DispatchResultWithPostInfo; } /// The trait implemented by the overarching enumeration of the different pallets' origins. /// Unlike `OriginTrait` impls, this does not include any kind of dispatch/call filter. Also, this /// trait is more flexible in terms of how it can be used: it is a `Parameter` and `Member`, so it /// can be used as dispatchable parameters as well as in storage items. pub trait CallerTrait: Parameter + Member + From> { /// Extract the signer from the message if it is a `Signed` origin. fn into_system(self) -> Option>; /// Extract a reference to the system-level `RawOrigin` if it is that. fn as_system_ref(&self) -> Option<&RawOrigin>; /// Extract the signer from it if a system `Signed` origin, `None` otherwise. fn as_signed(&self) -> Option<&AccountId> { self.as_system_ref().and_then(RawOrigin::as_signed) } /// Returns `true` if `self` is a system `Root` origin, `None` otherwise. fn is_root(&self) -> bool { self.as_system_ref().map_or(false, RawOrigin::is_root) } /// Returns `true` if `self` is a system `None` origin, `None` otherwise. fn is_none(&self) -> bool { self.as_system_ref().map_or(false, RawOrigin::is_none) } } /// Methods available on `frame_system::Config::RuntimeOrigin`. 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: Into + CallerTrait + MaxEncodedLen; /// 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::1fig::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 caller is not root, if false is returned then the call must be filtered /// out. /// /// For root origin caller, the filters are bypassed and true is returned. fn filter_call(&self, call: &Self::Call) -> bool; /// Get a reference to the caller (`CallerTrait` impl). fn caller(&self) -> &Self::PalletsOrigin; /// Consume `self` and return the caller. fn into_caller(self) -> Self::PalletsOrigin; /// Do something with the caller, consuming self but returning it if the caller was unused. fn try_with_caller( self, f: impl FnOnce(Self::PalletsOrigin) -> Result, ) -> Result; /// Create with system none origin and `frame_system::Config::BaseCallFilter`. fn none() -> Self; /// Create with system root origin and `frame_system::Config::BaseCallFilter`. fn root() -> Self; /// Create with system signed origin and `frame_system::Config::BaseCallFilter`. fn signed(by: Self::AccountId) -> Self; /// Extract the signer from the message if it is a `Signed` origin. #[deprecated = "Use `into_signer` instead"] fn as_signed(self) -> Option { self.into_signer() } /// Extract the signer from the message if it is a `Signed` origin. fn into_signer(self) -> Option { self.into_caller().into_system().and_then(|s| { if let RawOrigin::Signed(who) = s { Some(who) } else { None } }) } /// Extract a reference to the system origin, if that's what the caller is. fn as_system_ref(&self) -> Option<&RawOrigin> { self.caller().as_system_ref() } } #[cfg(test)] mod tests { use super::*; use crate::traits::{ConstBool, ConstU8, TypedGet}; use std::marker::PhantomData; struct EnsureSuccess(PhantomData); struct EnsureFail(PhantomData); impl EnsureOrigin<()> for EnsureSuccess { type Success = V::Type; fn try_origin(_: ()) -> Result { Ok(V::get()) } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result<(), ()> { Ok(()) } } impl EnsureOrigin<()> for EnsureFail { type Success = T; fn try_origin(_: ()) -> Result { Err(()) } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result<(), ()> { Err(()) } } #[test] fn either_of_diverse_works() { assert_eq!( EitherOfDiverse::< EnsureSuccess>, EnsureSuccess>, >::try_origin(()).unwrap().left(), Some(true) ); assert_eq!( EitherOfDiverse::>, EnsureFail>::try_origin(()) .unwrap() .left(), Some(true) ); assert_eq!( EitherOfDiverse::, EnsureSuccess>>::try_origin(()) .unwrap() .right(), Some(0u8) ); assert!(EitherOfDiverse::, EnsureFail>::try_origin(()).is_err()); } #[test] fn either_of_works() { assert_eq!( EitherOf::< EnsureSuccess>, EnsureSuccess>, >::try_origin(()).unwrap(), true ); assert_eq!( EitherOf::>, EnsureFail>::try_origin(()).unwrap(), true ); assert_eq!( EitherOf::, EnsureSuccess>>::try_origin(()).unwrap(), false ); assert!(EitherOf::, EnsureFail>::try_origin(()).is_err()); } }