#
pezkuwi_sdk_docs/reference_docs/
frame_origin.rs

    1//! # FRAME Origin
    2//!
    3//! Let's start by clarifying a common wrong assumption about Origin:
    4//!
    5//! **ORIGIN IS NOT AN ACCOUNT ID**.
    6//!
    7//! FRAME's origin abstractions allow you to convey meanings far beyond just an account-id being the
    8//! caller of an extrinsic. Nonetheless, an account-id having signed an extrinsic is one of the
    9//! meanings that an origin can convey. This is the commonly used
    10//! [`pezframe_system::ensure_signed`], where the return value happens to be an account-id.
    11//!
    12//! Instead, let's establish the following as the correct definition of an origin:
    13//!
    14//! > The origin type represents the privilege level of the caller of an extrinsic.
    15//!
    16//! That is, an extrinsic, through checking the origin, can *express what privilege level it wishes
    17//! to impose on the caller of the extrinsic*. One of those checks can be as simple as "*any account
    18//! that has signed a statement can pass*".
    19//!
    20//! But the origin system can also express more abstract and complicated privilege levels. For
    21//! example:
    22//!
    23//! * If the majority of token holders agreed upon this. This is more or less what the
    24//!   [`pezpallet_democracy`] does under the hood ([reference](https://github.com/pezkuwichain/pezkuwi-sdk/blob/edd95b3749754d2ed0c5738588e872c87be91624/bizinikiwi/pezframe/democracy/src/lib.rs#L1603-L1633)).
    25//! * If a specific ratio of an instance of [`pezpallet_collective`]/DAO agrees upon this.
    26//! * If another consensus system, for example a bridged network or a teyrchain, agrees upon this.
    27//! * If the majority of validator/authority set agrees upon this[^1].
    28//! * If caller holds a particular NFT.
    29//!
    30//! and many more.
    31//!
    32//! ## Context
    33//!
    34//! First, let's look at where the `origin` type is encountered in a typical pezpallet. The `origin:
    35//! OriginFor<T>` has to be the first argument of any given callable extrinsic in FRAME:
    36#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", call_simple)]
    37//!
    38//! Typically, the code of an extrinsic starts with an origin check, such as
    39//! [`pezframe_system::ensure_signed`].
    40//!
    41//! Note that [`OriginFor`](pezframe_system::pezpallet_prelude::OriginFor) is merely a shorthand for
    42//! [`pezframe_system::Config::RuntimeOrigin`]. Given the name prefix `Runtime`, we can learn that
    43//! `RuntimeOrigin` is similar to `RuntimeCall` and others, a runtime composite enum that is
    44//! amalgamated at the runtime level. Read [`crate::reference_docs::frame_runtime_types`] to
    45//! familiarize yourself with these types.
    46//!
    47//! To understand this better, we will next create a pezpallet with a custom origin, which will add
    48//! a new variant to `RuntimeOrigin`.
    49//!
    50//! ## Adding Custom Pezpallet Origin to the Runtime
    51//!
    52//! For example, given a pezpallet that defines the following custom origin:
    53#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin)]
    54//!
    55//! And a runtime with the following pallets:
    56#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", runtime_exp)]
    57//!
    58//! The type [`crate::reference_docs::frame_origin::runtime_for_origin::RuntimeOrigin`] is expanded.
    59//! This `RuntimeOrigin` contains a variant for the [`pezframe_system::RawOrigin`] and the custom
    60//! origin of the pezpallet.
    61//!
    62//! > Notice how the [`pezframe_system::ensure_signed`] is nothing more than a `match` statement. If
    63//! > you want to know where the actual origin of an extrinsic is set (and the signature
    64//! > verification happens, if any), see
    65//! > [`pezsp_runtime::generic::CheckedExtrinsic#trait-implementations`], specifically
    66//! > [`pezsp_runtime::traits::Applyable`]'s implementation.
    67//!
    68//! ## Asserting on a Custom Internal Origin
    69//!
    70//! In order to assert on a custom origin that is defined within your pezpallet, we need a way to
    71//! first convert the `<T as pezframe_system::Config>::RuntimeOrigin` into the local `enum Origin`
    72//! of the current pezpallet. This is a common process that is explained in
    73//! [`crate::reference_docs::frame_runtime_types#
    74//! adding-further-constraints-to-runtime-composite-enums`].
    75//!
    76//! We use the same process here to express that `RuntimeOrigin` has a number of additional bounds,
    77//! as follows.
    78//!
    79//! 1. Defining a custom `RuntimeOrigin` with further bounds in the pezpallet.
    80#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin_bound)]
    81//!
    82//! 2. Using it in the pezpallet.
    83#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin_usage)]
    84//!
    85//! ## Asserting on a Custom External Origin
    86//!
    87//! Very often, a pezpallet wants to have a parameterized origin that is **NOT** defined within the
    88//! pezpallet. In other words, a pezpallet wants to delegate an origin check to something that is
    89//! specified later at the runtime level. Like many other parameterizations in FRAME, this implies
    90//! adding a new associated type to `trait Config`.
    91#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_def)]
    92//!
    93//! Then, within the pezpallet, we can simply use this "unknown" origin check type:
    94#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_usage)]
    95//!
    96//! Finally, at the runtime, any implementation of [`pezframe::traits::EnsureOrigin`] can be passed.
    97#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_provide)]
    98//!
    99//! Indeed, some of these implementations of [`pezframe::traits::EnsureOrigin`] are similar to the ones
    100//! that we know about: [`pezframe::runtime::prelude::EnsureSigned`],
    101//! [`pezframe::runtime::prelude::EnsureSignedBy`], [`pezframe::runtime::prelude::EnsureRoot`],
    102//! [`pezframe::runtime::prelude::EnsureNone`], etc. But, there are also many more that are not known
    103//! to us, and are defined in other pallets.
    104//!
    105//! For example, [`pezpallet_collective`] defines [`pezpallet_collective::EnsureMember`] and
    106//! [`pezpallet_collective::EnsureProportionMoreThan`] and many more, which is exactly what we
    107//! alluded to earlier in this document.
    108//!
    109//! Make sure to check the full list of [implementors of
    110//! `EnsureOrigin`](pezframe::traits::EnsureOrigin#implementors) for more inspiration.
    111//!
    112//! ## Obtaining Abstract Origins
    113//!
    114//! So far we have learned that FRAME pallets can assert on custom and abstract origin types,
    115//! whether they are defined within the pezpallet or not. But how can we obtain these abstract
    116//! origins?
    117//!
    118//! > All extrinsics that come from the outer world can generally only be obtained as either
    119//! > `signed` or `none` origin.
    120//!
    121//! Generally, these abstract origins are only obtained within the runtime, when a call is
    122//! dispatched within the runtime.
    123//!
    124//! ## Further References
    125//!
    126//! - [Gavin Wood's speech about FRAME features at Protocol Berg 2023.](https://youtu.be/j7b8Upipmeg?si=83_XUgYuJxMwWX4g&t=195)
    127//! - [A related StackExchange question.](https://exchange.pezkuwichain.app/questions/10992/how-do-you-find-the-public-key-for-the-medium-spender-track-origin)
    128//!
    129//! [^1]: Inherents are essentially unsigned extrinsics that need an [`pezframe_system::ensure_none`]
    130//! origin check, and through the virtue of being an inherent, are agreed upon by all validators.
    131
    132use pezframe::prelude::*;
    133
    134#[pezframe::pezpallet(dev_mode)]
    135pub mod pezpallet_for_origin {
    136	use super::*;
    137
    138	#[pezpallet::config]
    139	pub trait Config: pezframe_system::Config {}
    140
    141	#[pezpallet::pezpallet]
    142	pub struct Pezpallet<T>(_);
    143
    144	#[docify::export(call_simple)]
    145	#[pezpallet::call]
    146	impl<T: Config> Pezpallet<T> {
    147		pub fn do_something(_origin: OriginFor<T>) -> DispatchResult {
    148			//              ^^^^^^^^^^^^^^^^^^^^^
    149			todo!();
    150		}
    151	}
    152}
    153
    154#[pezframe::pezpallet(dev_mode)]
    155pub mod pezpallet_with_custom_origin {
    156	use super::*;
    157
    158	#[docify::export(custom_origin_bound)]
    159	#[pezpallet::config]
    160	pub trait Config: pezframe_system::Config {
    161		type RuntimeOrigin: From<<Self as pezframe_system::Config>::RuntimeOrigin>
    162			+ Into<Result<Origin, <Self as Config>::RuntimeOrigin>>;
    163	}
    164
    165	#[pezpallet::pezpallet]
    166	pub struct Pezpallet<T>(_);
    167
    168	#[docify::export(custom_origin)]
    169	/// A dummy custom origin.
    170	#[pezpallet::origin]
    171	#[derive(
    172		PartialEq,
    173		Eq,
    174		Clone,
    175		RuntimeDebug,
    176		Encode,
    177		Decode,
    178		DecodeWithMemTracking,
    179		TypeInfo,
    180		MaxEncodedLen,
    181	)]
    182	pub enum Origin {
    183		/// If all holders of a particular NFT have agreed upon this.
    184		AllNftHolders,
    185		/// If all validators have agreed upon this.
    186		ValidatorSet,
    187	}
    188
    189	#[docify::export(custom_origin_usage)]
    190	#[pezpallet::call]
    191	impl<T: Config> Pezpallet<T> {
    192		pub fn only_validators(origin: OriginFor<T>) -> DispatchResult {
    193			// first, we convert from `<T as pezframe_system::Config>::RuntimeOrigin` to `<T as
    194			// Config>::RuntimeOrigin`
    195			let local_runtime_origin = <<T as Config>::RuntimeOrigin as From<
    196				<T as pezframe_system::Config>::RuntimeOrigin,
    197			>>::from(origin);
    198			// then we convert to `origin`, if possible
    199			let local_origin =
    200				local_runtime_origin.into().map_err(|_| "invalid origin type provided")?;
    201			ensure!(matches!(local_origin, Origin::ValidatorSet), "Not authorized");
    202			todo!();
    203		}
    204	}
    205}
    206
    207pub mod runtime_for_origin {
    208	use super::pezpallet_with_custom_origin;
    209	use pezframe::{runtime::prelude::*, testing_prelude::*};
    210
    211	#[docify::export(runtime_exp)]
    212	construct_runtime!(
    213		pub struct Runtime {
    214			System: pezframe_system,
    215			PalletWithCustomOrigin: pezpallet_with_custom_origin,
    216		}
    217	);
    218
    219	#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
    220	impl pezframe_system::Config for Runtime {
    221		type Block = MockBlock<Self>;
    222	}
    223
    224	impl pezpallet_with_custom_origin::Config for Runtime {
    225		type RuntimeOrigin = RuntimeOrigin;
    226	}
    227}
    228
    229#[pezframe::pezpallet(dev_mode)]
    230pub mod pezpallet_with_external_origin {
    231	use super::*;
    232	#[docify::export(external_origin_def)]
    233	#[pezpallet::config]
    234	pub trait Config: pezframe_system::Config {
    235		type ExternalOrigin: EnsureOrigin<Self::RuntimeOrigin>;
    236	}
    237
    238	#[pezpallet::pezpallet]
    239	pub struct Pezpallet<T>(_);
    240
    241	#[docify::export(external_origin_usage)]
    242	#[pezpallet::call]
    243	impl<T: Config> Pezpallet<T> {
    244		pub fn externally_checked_ext(origin: OriginFor<T>) -> DispatchResult {
    245			T::ExternalOrigin::ensure_origin(origin)?;
    246			todo!();
    247		}
    248	}
    249}
    250
    251pub mod runtime_for_external_origin {
    252	use super::*;
    253	use pezframe::{runtime::prelude::*, testing_prelude::*};
    254
    255	construct_runtime!(
    256		pub struct Runtime {
    257			System: pezframe_system,
    258			PalletWithExternalOrigin: pezpallet_with_external_origin,
    259		}
    260	);
    261
    262	#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
    263	impl pezframe_system::Config for Runtime {
    264		type Block = MockBlock<Self>;
    265	}
    266
    267	#[docify::export(external_origin_provide)]
    268	impl pezpallet_with_external_origin::Config for Runtime {
    269		type ExternalOrigin = EnsureSigned<<Self as pezframe_system::Config>::AccountId>;
    270	}
    271}