Introduce EnsureOrigin::try_successul_origin (#11558)

* Introduce `EnsureOrigin::try_successul_origin`

* Formatting

* Fixes

* Add Morph

* Fixes

* Formatting
This commit is contained in:
Gavin Wood
2022-05-31 19:12:07 +01:00
committed by GitHub
parent adf0773f9d
commit 9107ae41fd
6 changed files with 221 additions and 97 deletions
+8 -8
View File
@@ -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)))
}
}
+3 -3
View File
@@ -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)))
}
}
+156 -68
View File
@@ -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(())
}
}
+16 -16
View File
@@ -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(())
}
}
+2 -2
View File
@@ -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())
}
}
@@ -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.