## Module frame_origin

Source
Expand description

Learn about Origins, a topic in FRAME that enables complex account abstractions to be built.

## §FRAME Origin

Let’s start by clarifying a common wrong assumption about Origin:

ORIGIN IS NOT AN ACCOUNT ID.

FRAME’s origin abstractions allow you to convey meanings far beyond just an account-id being the caller of an extrinsic. Nonetheless, an account-id having signed an extrinsic is one of the meanings that an origin can convey. This is the commonly used [`pezframe_system::ensure_signed`], where the return value happens to be an account-id.

Instead, let’s establish the following as the correct definition of an origin:

> The origin type represents the privilege level of the caller of an extrinsic.

That is, an extrinsic, through checking the origin, can express what privilege level it wishes to impose on the caller of the extrinsic. One of those checks can be as simple as “any account that has signed a statement can pass”.

But the origin system can also express more abstract and complicated privilege levels. For example:

- If the majority of token holders agreed upon this. This is more or less what the [`pezpallet_democracy`] does under the hood (reference).
- If a specific ratio of an instance of [`pezpallet_collective`]/DAO agrees upon this.
- If another consensus system, for example a bridged network or a teyrchain, agrees upon this.
- If the majority of validator/authority set agrees upon this1.
- If caller holds a particular NFT.

and many more.

### §Context

First, let’s look at where the `origin` type is encountered in a typical pezpallet. The `origin: OriginFor<T>` has to be the first argument of any given callable extrinsic in FRAME:

ⓘ

    #[pezpallet::call]
    impl<T: Config> Pezpallet<T> {
    	pub fn do_something(_origin: OriginFor<T>) -> DispatchResult {
    		//              ^^^^^^^^^^^^^^^^^^^^^
    		todo!();
    	}
    }

Typically, the code of an extrinsic starts with an origin check, such as [`pezframe_system::ensure_signed`].

Note that `OriginFor` is merely a shorthand for [`pezframe_system::Config::RuntimeOrigin`]. Given the name prefix `Runtime`, we can learn that `RuntimeOrigin` is similar to `RuntimeCall` and others, a runtime composite enum that is amalgamated at the runtime level. Read `crate::reference_docs::frame_runtime_types` to familiarize yourself with these types.

To understand this better, we will next create a pezpallet with a custom origin, which will add a new variant to `RuntimeOrigin`.

### §Adding Custom Pezpallet Origin to the Runtime

For example, given a pezpallet that defines the following custom origin:

ⓘ

    #[pezpallet::origin]
    #[derive(
    	PartialEq,
    	Eq,
    	Clone,
    	RuntimeDebug,
    	Encode,
    	Decode,
    	DecodeWithMemTracking,
    	TypeInfo,
    	MaxEncodedLen,
    )]
    pub enum Origin {
    	/// If all holders of a particular NFT have agreed upon this.
    	AllNftHolders,
    	/// If all validators have agreed upon this.
    	ValidatorSet,
    }

And a runtime with the following pallets:

ⓘ

    construct_runtime!(
    	pub struct Runtime {
    		System: pezframe_system,
    		PalletWithCustomOrigin: pezpallet_with_custom_origin,
    	}
    );

The type `crate::reference_docs::frame_origin::runtime_for_origin::RuntimeOrigin` is expanded. This `RuntimeOrigin` contains a variant for the [`pezframe_system::RawOrigin`] and the custom origin of the pezpallet.

> Notice how the [`pezframe_system::ensure_signed`] is nothing more than a `match` statement. If you want to know where the actual origin of an extrinsic is set (and the signature verification happens, if any), see [`pezsp_runtime::generic::CheckedExtrinsic#trait-implementations`], specifically [`pezsp_runtime::traits::Applyable`]’s implementation.

### §Asserting on a Custom Internal Origin

In order to assert on a custom origin that is defined within your pezpallet, we need a way to first convert the `<T as pezframe_system::Config>::RuntimeOrigin` into the local `enum Origin` of the current pezpallet. This is a common process that is explained in `crate::reference_docs::frame_runtime_types`.

We use the same process here to express that `RuntimeOrigin` has a number of additional bounds, as follows.

1. Defining a custom `RuntimeOrigin` with further bounds in the pezpallet.

ⓘ

    #[pezpallet::config]
    pub trait Config: pezframe_system::Config {
    	type RuntimeOrigin: From<<Self as pezframe_system::Config>::RuntimeOrigin>
    		+ Into<Result<Origin, <Self as Config>::RuntimeOrigin>>;
    }

2. Using it in the pezpallet.

ⓘ

    #[pezpallet::call]
    impl<T: Config> Pezpallet<T> {
    	pub fn only_validators(origin: OriginFor<T>) -> DispatchResult {
    		// first, we convert from `<T as pezframe_system::Config>::RuntimeOrigin` to `<T as
    		// Config>::RuntimeOrigin`
    		let local_runtime_origin = <<T as Config>::RuntimeOrigin as From<
    			<T as pezframe_system::Config>::RuntimeOrigin,
    		>>::from(origin);
    		// then we convert to `origin`, if possible
    		let local_origin =
    			local_runtime_origin.into().map_err(|_| "invalid origin type provided")?;
    		ensure!(matches!(local_origin, Origin::ValidatorSet), "Not authorized");
    		todo!();
    	}
    }

### §Asserting on a Custom External Origin

Very often, a pezpallet wants to have a parameterized origin that is NOT defined within the pezpallet. In other words, a pezpallet wants to delegate an origin check to something that is specified later at the runtime level. Like many other parameterizations in FRAME, this implies adding a new associated type to `trait Config`.

ⓘ

    #[pezpallet::config]
    pub trait Config: pezframe_system::Config {
    	type ExternalOrigin: EnsureOrigin<Self::RuntimeOrigin>;
    }

Then, within the pezpallet, we can simply use this “unknown” origin check type:

ⓘ

    #[pezpallet::call]
    impl<T: Config> Pezpallet<T> {
    	pub fn externally_checked_ext(origin: OriginFor<T>) -> DispatchResult {
    		T::ExternalOrigin::ensure_origin(origin)?;
    		todo!();
    	}
    }

Finally, at the runtime, any implementation of [`pezframe::traits::EnsureOrigin`] can be passed.

ⓘ

    impl pezpallet_with_external_origin::Config for Runtime {
    	type ExternalOrigin = EnsureSigned<<Self as pezframe_system::Config>::AccountId>;
    }

Indeed, some of these implementations of [`pezframe::traits::EnsureOrigin`] are similar to the ones that we know about: [`pezframe::runtime::prelude::EnsureSigned`], [`pezframe::runtime::prelude::EnsureSignedBy`], [`pezframe::runtime::prelude::EnsureRoot`], [`pezframe::runtime::prelude::EnsureNone`], etc. But, there are also many more that are not known to us, and are defined in other pallets.

For example, [`pezpallet_collective`] defines [`pezpallet_collective::EnsureMember`] and [`pezpallet_collective::EnsureProportionMoreThan`] and many more, which is exactly what we alluded to earlier in this document.

Make sure to check the full list of implementors of `EnsureOrigin` for more inspiration.

### §Obtaining Abstract Origins

So far we have learned that FRAME pallets can assert on custom and abstract origin types, whether they are defined within the pezpallet or not. But how can we obtain these abstract origins?

> All extrinsics that come from the outer world can generally only be obtained as either `signed` or `none` origin.

Generally, these abstract origins are only obtained within the runtime, when a call is dispatched within the runtime.

### §Further References

- Gavin Wood’s speech about FRAME features at Protocol Berg 2023.
- A related StackExchange question.

1. Inherents are essentially unsigned extrinsics that need an [`pezframe_system::ensure_none`] origin check, and through the virtue of being an inherent, are agreed upon by all validators. ↩

## Modules§

pezpallet_for_origin
  The `pezpallet` module in each FRAME pezpallet hosts the most important items needed to construct this pezpallet.
pezpallet_with_custom_origin
  The `pezpallet` module in each FRAME pezpallet hosts the most important items needed to construct this pezpallet.
pezpallet_with_external_origin
  The `pezpallet` module in each FRAME pezpallet hosts the most important items needed to construct this pezpallet.
runtime_for_external_origin
runtime_for_origin