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