Skip to main content

topsoil_core/traits/
dispatch.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! Traits for dealing with dispatching calls and the origin from which they are dispatched.
8
9use crate::dispatch::{DispatchResultWithPostInfo, Parameter, RawOrigin};
10use codec::MaxEncodedLen;
11use core::{cmp::Ordering, marker::PhantomData};
12use subsoil::runtime::{
13	traits::{BadOrigin, Get, Member, Morph, TryMorph},
14	transaction_validity::{TransactionSource, TransactionValidityError, ValidTransaction},
15	Either,
16};
17use subsoil::weights::Weight;
18
19use super::misc;
20
21/// Some sort of check on the origin is performed by this object.
22pub trait EnsureOrigin<OuterOrigin> {
23	/// A return type.
24	type Success;
25
26	/// Perform the origin check.
27	fn ensure_origin(o: OuterOrigin) -> Result<Self::Success, BadOrigin> {
28		Self::try_origin(o).map_err(|_| BadOrigin)
29	}
30
31	/// The same as `ensure_origin` except that Root origin will always pass. This can only be
32	/// used if `Success` has a sensible impl of `Default` since that will be used in the result.
33	fn ensure_origin_or_root(o: OuterOrigin) -> Result<Option<Self::Success>, BadOrigin>
34	where
35		OuterOrigin: OriginTrait,
36	{
37		if o.caller().is_root() {
38			return Ok(None);
39		} else {
40			Self::ensure_origin(o).map(Some)
41		}
42	}
43
44	/// Perform the origin check.
45	fn try_origin(o: OuterOrigin) -> Result<Self::Success, OuterOrigin>;
46
47	/// The same as `try_origin` except that Root origin will always pass. This can only be
48	/// used if `Success` has a sensible impl of `Default` since that will be used in the result.
49	fn try_origin_or_root(o: OuterOrigin) -> Result<Option<Self::Success>, OuterOrigin>
50	where
51		OuterOrigin: OriginTrait,
52	{
53		if o.caller().is_root() {
54			return Ok(None);
55		} else {
56			Self::try_origin(o).map(Some)
57		}
58	}
59
60	/// Attempt to get an outer origin capable of passing `try_origin` check. May return `Err` if it
61	/// is impossible.
62	///
63	/// ** Should be used for benchmarking only!!! **
64	#[cfg(feature = "runtime-benchmarks")]
65	fn try_successful_origin() -> Result<OuterOrigin, ()>;
66}
67
68/// [`EnsureOrigin`] implementation that checks that an origin has equal or higher privilege
69/// compared to the expected `Origin`.
70///
71/// It will take the shortcut of comparing the incoming origin with the expected `Origin` and if
72/// both are the same the origin is accepted.
73///
74/// # Example
75///
76/// ```rust
77/// # use topsoil_core::traits::{EnsureOriginEqualOrHigherPrivilege, PrivilegeCmp, EnsureOrigin as _};
78/// # use subsoil::runtime::traits::{parameter_types, Get};
79/// # use core::cmp::Ordering;
80///
81/// #[derive(Eq, PartialEq, Debug)]
82/// pub enum Origin {
83///     Root,
84///     SomethingBelowRoot,
85///     NormalUser,
86/// }
87///
88/// struct OriginPrivilegeCmp;
89///
90/// impl PrivilegeCmp<Origin> for OriginPrivilegeCmp {
91///     fn cmp_privilege(left: &Origin, right: &Origin) -> Option<Ordering> {
92///         match (left, right) {
93///             (Origin::Root, Origin::Root) => Some(Ordering::Equal),
94///             (Origin::Root, _) => Some(Ordering::Greater),
95///             (Origin::SomethingBelowRoot, Origin::SomethingBelowRoot) => Some(Ordering::Equal),
96///             (Origin::SomethingBelowRoot, Origin::Root) => Some(Ordering::Less),
97///             (Origin::SomethingBelowRoot, Origin::NormalUser) => Some(Ordering::Greater),
98///             (Origin::NormalUser, Origin::NormalUser) => Some(Ordering::Equal),
99///             (Origin::NormalUser, _) => Some(Ordering::Less),
100///         }
101///     }
102/// }
103///
104/// parameter_types! {
105///     pub const ExpectedOrigin: Origin = Origin::SomethingBelowRoot;
106/// }
107///
108/// type EnsureOrigin = EnsureOriginEqualOrHigherPrivilege<ExpectedOrigin, OriginPrivilegeCmp>;
109///
110/// // `Root` has an higher privilege as our expected origin.
111/// assert!(EnsureOrigin::ensure_origin(Origin::Root).is_ok());
112/// // `SomethingBelowRoot` is exactly the expected origin.
113/// assert!(EnsureOrigin::ensure_origin(Origin::SomethingBelowRoot).is_ok());
114/// // The `NormalUser` origin is not allowed.
115/// assert!(EnsureOrigin::ensure_origin(Origin::NormalUser).is_err());
116/// ```
117pub struct EnsureOriginEqualOrHigherPrivilege<Origin, PrivilegeCmp>(
118	core::marker::PhantomData<(Origin, PrivilegeCmp)>,
119);
120
121impl<OuterOrigin, Origin, PrivilegeCmp> EnsureOrigin<OuterOrigin>
122	for EnsureOriginEqualOrHigherPrivilege<Origin, PrivilegeCmp>
123where
124	Origin: Get<OuterOrigin>,
125	OuterOrigin: Eq,
126	PrivilegeCmp: misc::PrivilegeCmp<OuterOrigin>,
127{
128	type Success = ();
129
130	fn try_origin(o: OuterOrigin) -> Result<Self::Success, OuterOrigin> {
131		let expected_origin = Origin::get();
132
133		// If this is the expected origin, it has the same privilege.
134		if o == expected_origin {
135			return Ok(());
136		}
137
138		let cmp = PrivilegeCmp::cmp_privilege(&o, &expected_origin);
139
140		match cmp {
141			Some(Ordering::Equal) | Some(Ordering::Greater) => Ok(()),
142			None | Some(Ordering::Less) => Err(o),
143		}
144	}
145
146	#[cfg(feature = "runtime-benchmarks")]
147	fn try_successful_origin() -> Result<OuterOrigin, ()> {
148		Ok(Origin::get())
149	}
150}
151
152/// Some sort of check on the origin is performed by this object.
153pub trait EnsureOriginWithArg<OuterOrigin, Argument> {
154	/// A return type.
155	type Success;
156
157	/// Perform the origin check.
158	fn ensure_origin(o: OuterOrigin, a: &Argument) -> Result<Self::Success, BadOrigin> {
159		Self::try_origin(o, a).map_err(|_| BadOrigin)
160	}
161
162	/// Perform the origin check, returning the origin value if unsuccessful. This allows chaining.
163	fn try_origin(o: OuterOrigin, a: &Argument) -> Result<Self::Success, OuterOrigin>;
164
165	/// Attempt to get an outer origin capable of passing `try_origin` check. May return `Err` if it
166	/// is impossible.
167	///
168	/// ** Should be used for benchmarking only!!! **
169	#[cfg(feature = "runtime-benchmarks")]
170	fn try_successful_origin(a: &Argument) -> Result<OuterOrigin, ()>;
171}
172
173/// Simple macro to explicitly implement [EnsureOriginWithArg] to be used on any type which
174/// implements [EnsureOrigin]. This is quick and dirty, so you must use the type parameters `O`
175/// (the origin type), `T` (the argument type) and `AccountId` (if you are using the `O: ..` form).
176///
177/// The argument is ignored, much like in [AsEnsureOriginWithArg].
178#[macro_export]
179macro_rules! impl_ensure_origin_with_arg_ignoring_arg {
180	( impl < { O: .., I: 'static, $( $bound:tt )* }> EnsureOriginWithArg<O, $t_param:ty> for $name:ty {} ) => {
181		impl_ensure_origin_with_arg_ignoring_arg! {
182			impl <{
183				O: $crate::traits::OriginTrait,
184				I: 'static,
185				$( $bound )*
186			}> EnsureOriginWithArg<O, $t_param> for $name {}
187		}
188	};
189	( impl < { O: .. , $( $bound:tt )* }> EnsureOriginWithArg<O, $t_param:ty> for $name:ty {} ) => {
190		impl_ensure_origin_with_arg_ignoring_arg! {
191			impl <{
192				O: $crate::traits::OriginTrait,
193				$( $bound )*
194			}> EnsureOriginWithArg<O, $t_param> for $name {}
195		}
196	};
197	( impl < { $( $bound:tt )* } > EnsureOriginWithArg<$o_param:ty, $t_param:ty> for $name:ty {} ) => {
198		impl < $( $bound )* > EnsureOriginWithArg<$o_param, $t_param> for $name {
199			type Success = <Self as EnsureOrigin<$o_param>>::Success;
200			fn try_origin(o: $o_param, _: &$t_param) -> Result<Self::Success, $o_param> {
201				<Self as EnsureOrigin<$o_param>>::try_origin(o)
202			}
203			#[cfg(feature = "runtime-benchmarks")]
204			fn try_successful_origin(_: &$t_param) -> Result<$o_param, ()> {
205				<Self as EnsureOrigin<$o_param>>::try_successful_origin()
206			}
207		}
208	}
209}
210
211/// [`EnsureOrigin`] implementation that always fails.
212pub struct NeverEnsureOrigin<Success>(core::marker::PhantomData<Success>);
213impl<OO, Success> EnsureOrigin<OO> for NeverEnsureOrigin<Success> {
214	type Success = Success;
215	fn try_origin(o: OO) -> Result<Success, OO> {
216		Err(o)
217	}
218	#[cfg(feature = "runtime-benchmarks")]
219	fn try_successful_origin() -> Result<OO, ()> {
220		Err(())
221	}
222}
223impl_ensure_origin_with_arg_ignoring_arg! {
224	impl<{ OO, Success, A }>
225		EnsureOriginWithArg<OO, A> for NeverEnsureOrigin<Success>
226	{}
227}
228
229pub struct AsEnsureOriginWithArg<EO>(core::marker::PhantomData<EO>);
230impl<OuterOrigin, Argument, EO: EnsureOrigin<OuterOrigin>>
231	EnsureOriginWithArg<OuterOrigin, Argument> for AsEnsureOriginWithArg<EO>
232{
233	/// A return type.
234	type Success = EO::Success;
235
236	/// Perform the origin check.
237	fn ensure_origin(o: OuterOrigin, _: &Argument) -> Result<Self::Success, BadOrigin> {
238		EO::ensure_origin(o)
239	}
240
241	/// Perform the origin check, returning the origin value if unsuccessful. This allows chaining.
242	fn try_origin(o: OuterOrigin, _: &Argument) -> Result<Self::Success, OuterOrigin> {
243		EO::try_origin(o)
244	}
245
246	/// Returns an outer origin capable of passing `try_origin` check.
247	///
248	/// ** Should be used for benchmarking only!!! **
249	#[cfg(feature = "runtime-benchmarks")]
250	fn try_successful_origin(_: &Argument) -> Result<OuterOrigin, ()> {
251		EO::try_successful_origin()
252	}
253}
254
255/// A derivative `EnsureOrigin` implementation. It mutates the `Success` result of an `Original`
256/// implementation with a given `Mutator`.
257pub struct MapSuccess<Original, Mutator>(PhantomData<(Original, Mutator)>);
258impl<O, Original: EnsureOrigin<O>, Mutator: Morph<Original::Success>> EnsureOrigin<O>
259	for MapSuccess<Original, Mutator>
260{
261	type Success = Mutator::Outcome;
262	fn try_origin(o: O) -> Result<Mutator::Outcome, O> {
263		Ok(Mutator::morph(Original::try_origin(o)?))
264	}
265	#[cfg(feature = "runtime-benchmarks")]
266	fn try_successful_origin() -> Result<O, ()> {
267		Original::try_successful_origin()
268	}
269}
270impl<O, Original: EnsureOriginWithArg<O, A>, Mutator: Morph<Original::Success>, A>
271	EnsureOriginWithArg<O, A> for MapSuccess<Original, Mutator>
272{
273	type Success = Mutator::Outcome;
274	fn try_origin(o: O, a: &A) -> Result<Mutator::Outcome, O> {
275		Ok(Mutator::morph(Original::try_origin(o, a)?))
276	}
277	#[cfg(feature = "runtime-benchmarks")]
278	fn try_successful_origin(a: &A) -> Result<O, ()> {
279		Original::try_successful_origin(a)
280	}
281}
282
283/// A derivative `EnsureOrigin` implementation. It mutates the `Success` result of an `Original`
284/// implementation with a given `Mutator`, allowing the possibility of an error to be returned
285/// from the mutator.
286///
287/// NOTE: This is strictly worse performance than `MapSuccess` since it clones the original origin
288/// value. If possible, use `MapSuccess` instead.
289pub struct TryMapSuccess<Orig, Mutator>(PhantomData<(Orig, Mutator)>);
290impl<O: Clone, Original: EnsureOrigin<O>, Mutator: TryMorph<Original::Success>> EnsureOrigin<O>
291	for TryMapSuccess<Original, Mutator>
292{
293	type Success = Mutator::Outcome;
294	fn try_origin(o: O) -> Result<Mutator::Outcome, O> {
295		let orig = o.clone();
296		Mutator::try_morph(Original::try_origin(o)?).map_err(|()| orig)
297	}
298	#[cfg(feature = "runtime-benchmarks")]
299	fn try_successful_origin() -> Result<O, ()> {
300		Original::try_successful_origin()
301	}
302}
303impl<O: Clone, Original: EnsureOriginWithArg<O, A>, Mutator: TryMorph<Original::Success>, A>
304	EnsureOriginWithArg<O, A> for TryMapSuccess<Original, Mutator>
305{
306	type Success = Mutator::Outcome;
307	fn try_origin(o: O, a: &A) -> Result<Mutator::Outcome, O> {
308		let orig = o.clone();
309		Mutator::try_morph(Original::try_origin(o, a)?).map_err(|()| orig)
310	}
311	#[cfg(feature = "runtime-benchmarks")]
312	fn try_successful_origin(a: &A) -> Result<O, ()> {
313		Original::try_successful_origin(a)
314	}
315}
316
317pub struct TryWithMorphedArg<O, A, Morph, Inner, Success>(
318	PhantomData<(O, A, Morph, Inner, Success)>,
319);
320impl<
321		O,
322		A,
323		Morph: for<'a> TryMorph<&'a A>,
324		Inner: for<'a> EnsureOriginWithArg<O, <Morph as TryMorph<&'a A>>::Outcome, Success = Success>,
325		Success,
326	> EnsureOriginWithArg<O, A> for TryWithMorphedArg<O, A, Morph, Inner, Success>
327{
328	type Success = Success;
329	fn try_origin(o: O, a: &A) -> Result<Success, O> {
330		match Morph::try_morph(a) {
331			Ok(x) => Inner::try_origin(o, &x),
332			_ => return Err(o),
333		}
334	}
335	#[cfg(feature = "runtime-benchmarks")]
336	fn try_successful_origin(a: &A) -> Result<O, ()> {
337		Inner::try_successful_origin(&Morph::try_morph(a).map_err(|_| ())?)
338	}
339}
340
341/// "OR gate" implementation of `EnsureOrigin` allowing for different `Success` types for `L`
342/// and `R`, with them combined using an `Either` type.
343///
344/// Origin check will pass if `L` or `R` origin check passes. `L` is tested first.
345///
346/// Successful origin is derived from the left side.
347pub struct EitherOfDiverse<L, R>(core::marker::PhantomData<(L, R)>);
348impl<OuterOrigin, L: EnsureOrigin<OuterOrigin>, R: EnsureOrigin<OuterOrigin>>
349	EnsureOrigin<OuterOrigin> for EitherOfDiverse<L, R>
350{
351	type Success = Either<L::Success, R::Success>;
352	fn try_origin(o: OuterOrigin) -> Result<Self::Success, OuterOrigin> {
353		L::try_origin(o)
354			.map_or_else(|o| R::try_origin(o).map(Either::Right), |o| Ok(Either::Left(o)))
355	}
356
357	#[cfg(feature = "runtime-benchmarks")]
358	fn try_successful_origin() -> Result<OuterOrigin, ()> {
359		L::try_successful_origin().or_else(|()| R::try_successful_origin())
360	}
361}
362impl<
363		OuterOrigin,
364		L: EnsureOriginWithArg<OuterOrigin, Argument>,
365		R: EnsureOriginWithArg<OuterOrigin, Argument>,
366		Argument,
367	> EnsureOriginWithArg<OuterOrigin, Argument> for EitherOfDiverse<L, R>
368{
369	type Success = Either<L::Success, R::Success>;
370	fn try_origin(o: OuterOrigin, a: &Argument) -> Result<Self::Success, OuterOrigin> {
371		L::try_origin(o, a)
372			.map_or_else(|o| R::try_origin(o, a).map(Either::Right), |o| Ok(Either::Left(o)))
373	}
374
375	#[cfg(feature = "runtime-benchmarks")]
376	fn try_successful_origin(a: &Argument) -> Result<OuterOrigin, ()> {
377		L::try_successful_origin(a).or_else(|()| R::try_successful_origin(a))
378	}
379}
380
381/// "OR gate" implementation of `EnsureOrigin` allowing for different `Success` types for `L`
382/// and `R`, with them combined using an `Either` type.
383///
384/// Origin check will pass if `L` or `R` origin check passes. `L` is tested first.
385///
386/// Successful origin is derived from the left side.
387#[deprecated = "Use `EitherOfDiverse` instead"]
388pub type EnsureOneOf<L, R> = EitherOfDiverse<L, R>;
389
390/// "OR gate" implementation of `EnsureOrigin`, `Success` type for both `L` and `R` must
391/// be equal.
392///
393/// Origin check will pass if `L` or `R` origin check passes. `L` is tested first.
394///
395/// Successful origin is derived from the left side.
396pub struct EitherOf<L, R>(core::marker::PhantomData<(L, R)>);
397impl<
398		OuterOrigin,
399		L: EnsureOrigin<OuterOrigin>,
400		R: EnsureOrigin<OuterOrigin, Success = L::Success>,
401	> EnsureOrigin<OuterOrigin> for EitherOf<L, R>
402{
403	type Success = L::Success;
404	fn try_origin(o: OuterOrigin) -> Result<Self::Success, OuterOrigin> {
405		L::try_origin(o).or_else(|o| R::try_origin(o))
406	}
407
408	#[cfg(feature = "runtime-benchmarks")]
409	fn try_successful_origin() -> Result<OuterOrigin, ()> {
410		L::try_successful_origin().or_else(|()| R::try_successful_origin())
411	}
412}
413impl<
414		OuterOrigin,
415		L: EnsureOriginWithArg<OuterOrigin, Argument>,
416		R: EnsureOriginWithArg<OuterOrigin, Argument, Success = L::Success>,
417		Argument,
418	> EnsureOriginWithArg<OuterOrigin, Argument> for EitherOf<L, R>
419{
420	type Success = L::Success;
421	fn try_origin(o: OuterOrigin, a: &Argument) -> Result<Self::Success, OuterOrigin> {
422		L::try_origin(o, a).or_else(|o| R::try_origin(o, a))
423	}
424
425	#[cfg(feature = "runtime-benchmarks")]
426	fn try_successful_origin(a: &Argument) -> Result<OuterOrigin, ()> {
427		L::try_successful_origin(a).or_else(|()| R::try_successful_origin(a))
428	}
429}
430
431/// Type that can be dispatched with an origin but without checking the origin filter.
432///
433/// Implemented for pallet dispatchable type by `decl_module` and for runtime dispatchable by
434/// `construct_runtime`.
435pub trait UnfilteredDispatchable {
436	/// The origin type of the runtime, (i.e. `topsoil_core::system::Config::RuntimeOrigin`).
437	type RuntimeOrigin;
438
439	/// Dispatch this call but do not check the filter in origin.
440	fn dispatch_bypass_filter(self, origin: Self::RuntimeOrigin) -> DispatchResultWithPostInfo;
441}
442
443/// The trait implemented by the overarching enumeration of the different pallets' origins.
444/// Unlike `OriginTrait` impls, this does not include any kind of dispatch/call filter. Also, this
445/// trait is more flexible in terms of how it can be used: it is a `Parameter` and `Member`, so it
446/// can be used as dispatchable parameters as well as in storage items.
447pub trait CallerTrait<AccountId>: Parameter + Member + From<RawOrigin<AccountId>> {
448	/// Extract the signer from the message if it is a `Signed` origin.
449	fn into_system(self) -> Option<RawOrigin<AccountId>>;
450
451	/// Extract a reference to the system-level `RawOrigin` if it is that.
452	fn as_system_ref(&self) -> Option<&RawOrigin<AccountId>>;
453
454	/// Extract the signer from it if a system `Signed` origin, `None` otherwise.
455	fn as_signed(&self) -> Option<&AccountId> {
456		self.as_system_ref().and_then(RawOrigin::as_signed)
457	}
458
459	/// Returns `true` if `self` is a system `Root` origin, `None` otherwise.
460	fn is_root(&self) -> bool {
461		self.as_system_ref().map_or(false, RawOrigin::is_root)
462	}
463
464	/// Returns `true` if `self` is a system `None` origin, `None` otherwise.
465	fn is_none(&self) -> bool {
466		self.as_system_ref().map_or(false, RawOrigin::is_none)
467	}
468}
469
470/// Methods available on `topsoil_core::system::Config::RuntimeOrigin`.
471pub trait OriginTrait: Sized {
472	/// Runtime call type, as in `topsoil_core::system::Config::Call`
473	type Call;
474
475	/// The caller origin, overarching type of all pallets origins.
476	type PalletsOrigin: Send + Sync + Into<Self> + CallerTrait<Self::AccountId> + MaxEncodedLen;
477
478	/// The AccountId used across the system.
479	type AccountId;
480
481	/// Add a filter to the origin.
482	fn add_filter(&mut self, filter: impl Fn(&Self::Call) -> bool + 'static);
483
484	/// Reset origin filters to default one, i.e `topsoil_core::system::1fig::BaseCallFilter`.
485	fn reset_filter(&mut self);
486
487	/// Replace the caller with caller from the other origin
488	fn set_caller_from(&mut self, other: impl Into<Self>);
489
490	/// Replace the caller with caller from the other origin
491	fn set_caller(&mut self, caller: Self::PalletsOrigin);
492
493	/// Replace the caller with caller from the other origin
494	fn set_caller_from_signed(&mut self, caller_account: Self::AccountId) {
495		self.set_caller(Self::PalletsOrigin::from(RawOrigin::Signed(caller_account)))
496	}
497
498	/// Filter the call if caller is not root, if false is returned then the call must be filtered
499	/// out.
500	///
501	/// For root origin caller, the filters are bypassed and true is returned.
502	fn filter_call(&self, call: &Self::Call) -> bool;
503
504	/// Get a reference to the caller (`CallerTrait` impl).
505	fn caller(&self) -> &Self::PalletsOrigin;
506
507	/// Consume `self` and return the caller.
508	fn into_caller(self) -> Self::PalletsOrigin;
509
510	/// Do something with the caller, consuming self but returning it if the caller was unused.
511	fn try_with_caller<R>(
512		self,
513		f: impl FnOnce(Self::PalletsOrigin) -> Result<R, Self::PalletsOrigin>,
514	) -> Result<R, Self>;
515
516	/// Create with system none origin and `topsoil_core::system::Config::BaseCallFilter`.
517	fn none() -> Self;
518
519	/// Create with system root origin and `topsoil_core::system::Config::BaseCallFilter`.
520	fn root() -> Self;
521
522	/// Create with system signed origin and `topsoil_core::system::Config::BaseCallFilter`.
523	fn signed(by: Self::AccountId) -> Self;
524
525	/// Extract the signer from the message if it is a `Signed` origin.
526	#[deprecated = "Use `into_signer` instead"]
527	fn as_signed(self) -> Option<Self::AccountId> {
528		self.into_signer()
529	}
530
531	/// Extract the signer from the message if it is a `Signed` origin.
532	fn into_signer(self) -> Option<Self::AccountId> {
533		self.into_caller().into_system().and_then(|s| {
534			if let RawOrigin::Signed(who) = s {
535				Some(who)
536			} else {
537				None
538			}
539		})
540	}
541
542	/// Extract a reference to the system origin, if that's what the caller is.
543	fn as_system_ref(&self) -> Option<&RawOrigin<Self::AccountId>> {
544		self.caller().as_system_ref()
545	}
546
547	/// Extract a reference to the signer, if that's what the caller is.
548	fn as_signer(&self) -> Option<&Self::AccountId> {
549		self.caller().as_system_ref().and_then(|s| {
550			if let RawOrigin::Signed(ref who) = s {
551				Some(who)
552			} else {
553				None
554			}
555		})
556	}
557}
558
559/// A trait to allow calls to authorize themselves from the origin `None`.
560///
561/// It is implemented by the [`crate::pallet`] macro and used by the
562/// `topsoil_core::system::AuthorizeCall` transaction extension.
563///
564/// Pallet writers can declare the authorization logic for a call using the call attribute:
565/// [`crate::pallet_macros::authorize`].
566pub trait Authorize {
567	/// The authorize function.
568	///
569	/// Returns
570	/// * `Some(Ok((valid_transaction, unspent weight)))` if the call is successfully authorized,
571	/// * `Some(Err(error))` if the call authorization is invalid,
572	/// * `None` if the call doesn't provide any authorization.
573	fn authorize(
574		&self,
575		source: TransactionSource,
576	) -> Option<Result<(ValidTransaction, Weight), TransactionValidityError>>;
577
578	/// The weight of the authorization function.
579	fn weight_of_authorize(&self) -> Weight;
580}
581
582#[cfg(test)]
583mod tests {
584	use super::*;
585	use crate::traits::{ConstBool, ConstU8, TypedGet};
586	use std::marker::PhantomData;
587
588	struct EnsureSuccess<V>(PhantomData<V>);
589	struct EnsureFail<T>(PhantomData<T>);
590
591	impl<V: TypedGet> EnsureOrigin<()> for EnsureSuccess<V> {
592		type Success = V::Type;
593		fn try_origin(_: ()) -> Result<Self::Success, ()> {
594			Ok(V::get())
595		}
596		#[cfg(feature = "runtime-benchmarks")]
597		fn try_successful_origin() -> Result<(), ()> {
598			Ok(())
599		}
600	}
601
602	impl<T> EnsureOrigin<()> for EnsureFail<T> {
603		type Success = T;
604		fn try_origin(_: ()) -> Result<Self::Success, ()> {
605			Err(())
606		}
607		#[cfg(feature = "runtime-benchmarks")]
608		fn try_successful_origin() -> Result<(), ()> {
609			Err(())
610		}
611	}
612
613	#[test]
614	fn either_of_diverse_works() {
615		assert_eq!(
616			EitherOfDiverse::<
617				EnsureSuccess<ConstBool<true>>,
618				EnsureSuccess<ConstU8<0>>,
619			>::try_origin(()).unwrap().left(),
620			Some(true)
621		);
622		assert_eq!(
623			EitherOfDiverse::<EnsureSuccess<ConstBool<true>>, EnsureFail<u8>>::try_origin(())
624				.unwrap()
625				.left(),
626			Some(true)
627		);
628		assert_eq!(
629			EitherOfDiverse::<EnsureFail<bool>, EnsureSuccess<ConstU8<0>>>::try_origin(())
630				.unwrap()
631				.right(),
632			Some(0u8)
633		);
634		assert!(EitherOfDiverse::<EnsureFail<bool>, EnsureFail<u8>>::try_origin(()).is_err());
635	}
636
637	#[test]
638	fn either_of_works() {
639		assert_eq!(
640			EitherOf::<
641				EnsureSuccess<ConstBool<true>>,
642				EnsureSuccess<ConstBool<false>>,
643			>::try_origin(()).unwrap(),
644			true
645		);
646		assert_eq!(
647			EitherOf::<EnsureSuccess<ConstBool<true>>, EnsureFail<bool>>::try_origin(()).unwrap(),
648			true
649		);
650		assert_eq!(
651			EitherOf::<EnsureFail<bool>, EnsureSuccess<ConstBool<false>>>::try_origin(()).unwrap(),
652			false
653		);
654		assert!(EitherOf::<EnsureFail<bool>, EnsureFail<bool>>::try_origin(()).is_err());
655	}
656}