mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 03:01:07 +00:00
Add documentation around FRAME Origin (#3362)
Does the following: - Add a reference doc page named `frame_runtime_types`, which explains what types like `RuntimeOrigin`, `RuntimeCall` etc are. - On top of it, it adds a reference doc page called `frame_origin` which explains a few important patterns that we use around origins - And finally brushes up `#[frame::origin]` docs. - Updates the theme, sidebar and favicon to look like: <img width="1728" alt="Screenshot 2024-02-20 at 12 16 00" src="https://github.com/paritytech/polkadot-sdk/assets/5588131/6d60a16b-2081-411b-8869-43b91920cca9"> All of this was inspired by https://substrate.stackexchange.com/questions/10992/how-do-you-find-the-public-key-for-the-medium-spender-track-origin/10993 closes https://github.com/paritytech/polkadot-sdk-docs/issues/45 closes https://github.com/paritytech/polkadot-sdk-docs/issues/43 contributes / overlaps with https://github.com/paritytech/polkadot-sdk/pull/2638 cc @liamaharon deprecation companion: https://github.com/substrate-developer-hub/substrate-docs/pull/2131 pba-content companion: https://github.com/Polkadot-Blockchain-Academy/pba-content/pull/977 --------- Co-authored-by: Radha <86818441+DrW3RK@users.noreply.github.com> Co-authored-by: Sebastian Kunert <skunert49@gmail.com> Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com> Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>
This commit is contained in:
@@ -91,7 +91,7 @@ build-rustdoc:
|
||||
- .run-immediately
|
||||
variables:
|
||||
SKIP_WASM_BUILD: 1
|
||||
RUSTDOCFLAGS: ""
|
||||
RUSTDOCFLAGS: "--default-theme=ayu --html-in-header ./docs/sdk/headers/header.html --extend-css ./docs/sdk/headers/theme.css"
|
||||
artifacts:
|
||||
name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}-doc"
|
||||
when: on_success
|
||||
|
||||
Generated
+9
@@ -13384,11 +13384,20 @@ dependencies = [
|
||||
"cumulus-pallet-parachain-system",
|
||||
"docify",
|
||||
"frame",
|
||||
"frame-system",
|
||||
"kitchensink-runtime",
|
||||
"pallet-aura",
|
||||
"pallet-authorship",
|
||||
"pallet-balances",
|
||||
"pallet-collective",
|
||||
"pallet-default-config-example",
|
||||
"pallet-democracy",
|
||||
"pallet-examples",
|
||||
"pallet-multisig",
|
||||
"pallet-proxy",
|
||||
"pallet-timestamp",
|
||||
"pallet-transaction-payment",
|
||||
"pallet-utility",
|
||||
"parity-scale-codec",
|
||||
"sc-cli",
|
||||
"sc-client-db",
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ flowchart
|
||||
|
||||
devhub --> polkadot_sdk
|
||||
devhub --> reference_docs
|
||||
devhub --> tutorial
|
||||
devhub --> guides
|
||||
|
||||
polkadot_sdk --> substrate
|
||||
polkadot_sdk --> frame
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
flowchart LR
|
||||
RuntimeCall --"TryInto"--> PalletCall
|
||||
PalletCall --"Into"--> RuntimeCall
|
||||
+15
-5
@@ -17,7 +17,10 @@ workspace = true
|
||||
# Needed for all FRAME-based code
|
||||
parity-scale-codec = { version = "3.0.0", default-features = false }
|
||||
scale-info = { version = "2.6.0", default-features = false }
|
||||
frame = { path = "../../substrate/frame", features = ["experimental", "runtime"] }
|
||||
frame = { path = "../../substrate/frame", features = [
|
||||
"experimental",
|
||||
"runtime",
|
||||
] }
|
||||
pallet-examples = { path = "../../substrate/frame/examples" }
|
||||
pallet-default-config-example = { path = "../../substrate/frame/examples/default-config" }
|
||||
|
||||
@@ -52,7 +55,18 @@ cumulus-pallet-parachain-system = { path = "../../cumulus/pallets/parachain-syst
|
||||
] }
|
||||
parachain-info = { package = "staging-parachain-info", path = "../../cumulus/parachains/pallets/parachain-info" }
|
||||
pallet-aura = { path = "../../substrate/frame/aura", default-features = false }
|
||||
|
||||
# Pallets and FRAME internals
|
||||
pallet-timestamp = { path = "../../substrate/frame/timestamp" }
|
||||
pallet-balances = { path = "../../substrate/frame/balances" }
|
||||
pallet-transaction-payment = { path = "../../substrate/frame/transaction-payment" }
|
||||
pallet-utility = { path = "../../substrate/frame/utility" }
|
||||
pallet-multisig = { path = "../../substrate/frame/multisig" }
|
||||
pallet-proxy = { path = "../../substrate/frame/proxy" }
|
||||
pallet-authorship = { path = "../../substrate/frame/authorship" }
|
||||
pallet-collective = { path = "../../substrate/frame/collective" }
|
||||
pallet-democracy = { path = "../../substrate/frame/democracy" }
|
||||
frame-system = { path = "../../substrate/frame/system" }
|
||||
|
||||
# Primitives
|
||||
sp-io = { path = "../../substrate/primitives/io" }
|
||||
@@ -64,9 +78,5 @@ sp-runtime = { path = "../../substrate/primitives/runtime" }
|
||||
# XCM
|
||||
xcm = { package = "staging-xcm", path = "../../polkadot/xcm" }
|
||||
|
||||
[dev-dependencies]
|
||||
parity-scale-codec = "3.6.5"
|
||||
scale-info = "2.9.0"
|
||||
|
||||
[features]
|
||||
experimental = ["pallet-aura/experimental"]
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
<script>
|
||||
function createToC() {
|
||||
let sidebar = document.querySelector(".sidebar");
|
||||
let headers = document.querySelectorAll("#main-content h2, #main-content h3, #main-content h4");
|
||||
console.log(`detected polkadot_sdk_docs: headers: ${headers.length}`);
|
||||
|
||||
let toc = document.createElement("div");
|
||||
toc.classList.add("sidebar-table-of-contents");
|
||||
toc.appendChild(document.createElement("h2").appendChild(document.createTextNode("Table of Contents")).parentNode);
|
||||
|
||||
let modules = document.querySelectorAll("main .item-table a.mod");
|
||||
|
||||
// the first two headers are always junk
|
||||
headers.forEach(header => {
|
||||
let link = document.createElement("a");
|
||||
link.href = "#" + header.id;
|
||||
link.textContent = header.textContent;
|
||||
link.className = header.tagName.toLowerCase();
|
||||
|
||||
toc.appendChild(link);
|
||||
|
||||
if (header.id == "modules" && header.textContent == "Modules") {
|
||||
modules.forEach(module => {
|
||||
let link = document.createElement("a");
|
||||
link.href = module.href;
|
||||
link.textContent = module.textContent;
|
||||
link.className = "h3";
|
||||
|
||||
toc.appendChild(link);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// insert toc as the second child in sidebar
|
||||
let sidebar_children = sidebar.children;
|
||||
if (sidebar_children.length > 1) {
|
||||
sidebar.insertBefore(toc, sidebar_children[1]);
|
||||
} else {
|
||||
sidebar.appendChild(toc);
|
||||
}
|
||||
}
|
||||
|
||||
function hideSidebarElements() {
|
||||
// Create the 'Expand for More' button
|
||||
var expandButton = document.createElement('button');
|
||||
expandButton.innerText = 'Expand More Items';
|
||||
expandButton.classList.add('expand-button');
|
||||
|
||||
// Insert the button at the top of the sidebar or before the '.sidebar-elems'
|
||||
var sidebarElems = document.querySelector('.sidebar-elems');
|
||||
sidebarElems.parentNode.insertBefore(expandButton, sidebarElems);
|
||||
|
||||
// Initially hide the '.sidebar-elems'
|
||||
sidebarElems.style.display = 'none';
|
||||
|
||||
// Add click event listener to the button
|
||||
expandButton.addEventListener('click', function() {
|
||||
// Toggle the display of the '.sidebar-elems'
|
||||
if (sidebarElems.style.display === 'none') {
|
||||
sidebarElems.style.display = 'block';
|
||||
expandButton.innerText = 'Collapse';
|
||||
} else {
|
||||
sidebarElems.style.display = 'none';
|
||||
expandButton.innerText = 'Expand for More';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", (event) => {
|
||||
// if the crate is one that starts with `polkadot_sdk_docs`
|
||||
let crate_name = document.querySelector("#main-content > div > h1 > a:nth-child(1)");
|
||||
if (!crate_name.textContent.startsWith("polkadot_sdk_docs")) {
|
||||
console.log("skipping -- not `polkadot_sdk_docs`");
|
||||
return;
|
||||
}
|
||||
|
||||
createToC();
|
||||
hideSidebarElements();
|
||||
|
||||
console.log("updating page based on being `polkadot_sdk_docs` crate");
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
nav.side-bar {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.sidebar-table-of-contents {
|
||||
margin-bottom: 1em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.sidebar-table-of-contents a {
|
||||
display: block;
|
||||
margin: 0.2em 0;
|
||||
}
|
||||
|
||||
.sidebar-table-of-contents .h2 {
|
||||
font-weight: bold;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.sidebar-table-of-contents .h3 {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.sidebar-table-of-contents .h4 {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
.sidebar h2.location {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar-elems {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Center the 'Expand for More' button */
|
||||
.expand-button {
|
||||
display: inline-block; /* Use inline-block for sizing */
|
||||
margin: 10px auto; /* Auto margins for horizontal centering */
|
||||
padding: 5px 10px;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
width: auto;
|
||||
/* Centering the button within its parent container */
|
||||
position: relative;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,16 @@
|
||||
:root {
|
||||
--polkadot-pink: #E6007A ;
|
||||
--polkadot-green: #56F39A ;
|
||||
--polkadot-lime: #D3FF33 ;
|
||||
--polkadot-cyan: #00B2FF ;
|
||||
--polkadot-purple: #552BBF ;
|
||||
}
|
||||
|
||||
body > nav.sidebar > div.sidebar-crate > a > img {
|
||||
/* logo width, given that the sidebar width is 200px; */
|
||||
width: 190px;
|
||||
}
|
||||
|
||||
body nav.sidebar {
|
||||
flex: 0 0 250px;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<script>
|
||||
window.addEventListener("DOMContentLoaded", (event) => {
|
||||
// if the crate is one that starts with `polkadot_sdk_docs`
|
||||
let crate_name = document.querySelector("#main-content > div > h1 > a:nth-child(1)");
|
||||
if (!crate_name.textContent.startsWith("polkadot_sdk_docs")) {
|
||||
console.log("skipping -- not `polkadot_sdk_docs`");
|
||||
return;
|
||||
}
|
||||
|
||||
let sidebar = document.querySelector(".sidebar");
|
||||
let headers = document.querySelectorAll("#main-content h2, #main-content h3, #main-content h4");
|
||||
console.log(`detected polkadot_sdk_docs: headers: ${headers.length}`);
|
||||
|
||||
let toc = document.createElement("div");
|
||||
toc.classList.add("table-of-contents");
|
||||
toc.appendChild(document.createElement("h2").appendChild(document.createTextNode("Table of Contents")).parentNode);
|
||||
|
||||
// the first two headers are always junk
|
||||
headers.forEach(header => {
|
||||
let link = document.createElement("a");
|
||||
link.href = "#" + header.id;
|
||||
link.textContent = header.textContent;
|
||||
link.className = header.tagName.toLowerCase();
|
||||
toc.appendChild(link);
|
||||
});
|
||||
|
||||
sidebar.insertBefore(toc, sidebar.firstChild);
|
||||
console.log("injecting ToC");
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
.table-of-contents {
|
||||
margin-bottom: 1em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.table-of-contents a {
|
||||
display: block;
|
||||
margin: 0.2em 0;
|
||||
}
|
||||
|
||||
.table-of-contents .h2 {
|
||||
font-weight: bold;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.table-of-contents .h3 {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.table-of-contents .h4 {
|
||||
margin-left: 2em;
|
||||
}
|
||||
</style>
|
||||
@@ -128,8 +128,8 @@
|
||||
//!
|
||||
//! Recall that within our pallet, (almost) all blocks of code are generic over `<T: Config>`. And,
|
||||
//! because `trait Config: frame_system::Config`, we can get access to all items in `Config` (or
|
||||
//! `frame_system::Config`) using `T::NameOfItem`. This is all within the boundaries of how Rust
|
||||
//! traits and generics work. If unfamiliar with this pattern, read
|
||||
//! `frame_system::Config`) using `T::NameOfItem`. This is all within the boundaries of how
|
||||
//! Rust traits and generics work. If unfamiliar with this pattern, read
|
||||
//! [`crate::reference_docs::trait_based_programming`] before going further.
|
||||
//!
|
||||
//! Crucially, a typical FRAME runtime contains a `struct Runtime`. The main role of this `struct`
|
||||
@@ -270,7 +270,7 @@
|
||||
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", config_v2)]
|
||||
//!
|
||||
//! > These `Runtime*` types are better explained in
|
||||
//! > [`crate::reference_docs::frame_composite_enums`].
|
||||
//! > [`crate::reference_docs::frame_runtime_types`].
|
||||
//!
|
||||
//! Then, we can rewrite the `transfer` dispatchable as such:
|
||||
#![doc = docify::embed!("./src/guides/your_first_pallet/mod.rs", transfer_v2)]
|
||||
@@ -285,7 +285,6 @@
|
||||
//! [`crate::guides::your_first_pallet::pallet_v2::tests::runtime_v2::RuntimeEvent`].
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//! ## What Next?
|
||||
//!
|
||||
//! The following topics where used in this guide, but not covered in depth. It is suggested to
|
||||
@@ -293,7 +292,7 @@
|
||||
//!
|
||||
//! - [`crate::reference_docs::safe_defensive_programming`].
|
||||
//! - [`crate::reference_docs::frame_origin`].
|
||||
//! - [`crate::reference_docs::frame_composite_enums`].
|
||||
//! - [`crate::reference_docs::frame_runtime_types`].
|
||||
//! - The pallet we wrote in this guide was using `dev_mode`, learn more in
|
||||
//! [`frame::pallet_macros::config`].
|
||||
//! - Learn more about the individual pallet items/macros, such as event and errors and call, in
|
||||
|
||||
+6
-1
@@ -15,7 +15,7 @@
|
||||
//! - Start by learning about the the [`polkadot_sdk`], its structure and context.
|
||||
//! - Then, head over the [`guides`]. This modules contains in-depth guides about the most important
|
||||
//! user-journeys of the Polkadot SDK.
|
||||
//! - Whilst reading the guides, you might find back-links to [`crate::reference_docs`].
|
||||
//! - Whilst reading the guides, you might find back-links to [`reference_docs`].
|
||||
//! - Finally, <https://paritytech.github.io> is the parent website of this crate that contains the
|
||||
//! list of further tools related to the Polkadot SDK.
|
||||
//!
|
||||
@@ -25,6 +25,11 @@
|
||||
#![doc = simple_mermaid::mermaid!("../../mermaid/IA.mmd")]
|
||||
#![warn(rustdoc::broken_intra_doc_links)]
|
||||
#![warn(rustdoc::private_intra_doc_links)]
|
||||
#![doc(html_favicon_url = "https://polkadot.network/favicon-32x32.png")]
|
||||
#![doc(
|
||||
html_logo_url = "https://europe1.discourse-cdn.com/standard21/uploads/polkadot2/original/1X/eb57081e2bb7c39e5fcb1a98b443e423fa4448ae.svg"
|
||||
)]
|
||||
#![doc(issue_tracker_base_url = "https://github.com/paritytech/polkadot-sdk/issues")]
|
||||
|
||||
/// Meta information about this crate, how it is built, what principles dictates its evolution and
|
||||
/// how one can contribute to it.
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
//! * Before even getting started, what is with all of this `<T: Config>`? We link to
|
||||
//! [`crate::reference_docs::trait_based_programming`].
|
||||
//! * First, the name. Why is this called `pallet::call`? This goes back to `enum Call`, which is
|
||||
//! explained in [`crate::reference_docs::frame_composite_enums`]. Build on top of this!
|
||||
//! explained in [`crate::reference_docs::frame_runtime_types`]. Build on top of this!
|
||||
//! * Then, what is `origin`? Just an account id? [`crate::reference_docs::frame_origin`].
|
||||
//! * Then, what is `DispatchResult`? Why is this called *dispatch*? Probably something that can be
|
||||
//! explained in the documentation of [`frame::prelude::DispatchResult`].
|
||||
@@ -138,7 +138,9 @@
|
||||
//! injected, run:
|
||||
//!
|
||||
//! ```sh
|
||||
//! SKIP_WASM_BUILD=1 RUSTDOCFLAGS="--html-in-header $(pwd)/docs/sdk/headers/toc.html" cargo doc -p polkadot-sdk-docs --no-deps --open
|
||||
//! SKIP_WASM_BUILD=1 \
|
||||
//! RUSTDOCFLAGS="--html-in-header $(pwd)/docs/sdk/headers/header.html --extend-css $(pwd)/docs/sdk/headers/theme.css --default-theme=ayu" \
|
||||
//! cargo doc -p polkadot-sdk-docs --no-deps --open
|
||||
//! ```
|
||||
//!
|
||||
//! If even faster build time for docs is needed, you can temporarily remove most of the
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
//! runtimes, a call is represented as an enum of enums, where the outer enum represents the FRAME
|
||||
//! pallet being called, and the inner enum represents the call being made within that pallet, and
|
||||
//! any arguments to it. Read more about the call enum
|
||||
//! [here][crate::reference_docs::frame_composite_enums].
|
||||
//! [here][crate::reference_docs::frame_runtime_types].
|
||||
//!
|
||||
//! FRAME `Call` enums are automatically generated, and end up looking something like this:
|
||||
#![doc = docify::embed!("./src/reference_docs/extrinsic_encoding.rs", call_data)]
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
//! # FRAME Composite Enums
|
||||
@@ -1,14 +1,260 @@
|
||||
//! # FRAME Origin
|
||||
//!
|
||||
//! Notes:
|
||||
//! Let's start by clarifying a common wrong assumption about Origin:
|
||||
//!
|
||||
//! - Def talk about account abstraction and how it is a solved issue in frame. See Gav's talk in
|
||||
//! Protocol Berg 2023
|
||||
//! - system's raw origin, how it is amalgamated with other origins into one type
|
||||
//! [`frame_composite_enums`]
|
||||
//! - signed origin
|
||||
//! - unsigned origin, link to [`fee_less_runtime`]
|
||||
//! - Root origin, how no one can obtain it.
|
||||
//! - Abstract origin: how FRAME allows you to express "origin is 2/3 of the this body or 1/2 of
|
||||
//! that body or half of the token holders".
|
||||
//! - `type CustomOrigin: EnsureOrigin<_>` in pallets.
|
||||
//! **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 [`frame_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
|
||||
//! [`pallet_democracy`] does under the hood ([reference](https://github.com/paritytech/polkadot-sdk/blob/edd95b3749754d2ed0c5738588e872c87be91624/substrate/frame/democracy/src/lib.rs#L1603-L1633)).
|
||||
//! * If a specific ratio of an instance of [`pallet_collective`]/DAO agrees upon this.
|
||||
//! * If another consensus system, for example a bridged network or a parachain, agrees upon this.
|
||||
//! * If the majority of validator/authority set agrees upon this[^1].
|
||||
//! * If caller holds a particular NFT.
|
||||
//!
|
||||
//! and many more.
|
||||
//!
|
||||
//! ## Context
|
||||
//!
|
||||
//! First, let's look at where the `origin` type is encountered in a typical pallet. The `origin:
|
||||
//! OriginFor<T>` has to be the first argument of any given callable extrinsic in FRAME:
|
||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", call_simple)]
|
||||
//!
|
||||
//! Typically, the code of an extrinsic starts with an origin check, such as
|
||||
//! [`frame_system::ensure_signed`].
|
||||
//!
|
||||
//! Note that [`OriginFor`](frame_system::pallet_prelude::OriginFor) is merely a shorthand for
|
||||
//! [`frame_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 pallet with a custom origin, which will add a
|
||||
//! new variant to `RuntimeOrigin`.
|
||||
//!
|
||||
//! ## Adding Custom Pallet Origin to the Runtime
|
||||
//!
|
||||
//! For example, given a pallet that defines the following custom origin:
|
||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin)]
|
||||
//!
|
||||
//! And a runtime with the following pallets:
|
||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", runtime_exp)]
|
||||
//!
|
||||
//! The type [`crate::reference_docs::frame_origin::runtime_for_origin::RuntimeOrigin`] is expanded.
|
||||
//! This `RuntimeOrigin` contains a variant for the [`frame_system::RawOrigin`] and the custom
|
||||
//! origin of the pallet.
|
||||
//!
|
||||
//! > Notice how the [`frame_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
|
||||
//! > [`sp_runtime::generic::CheckedExtrinsic#trait-implementations`], specifically
|
||||
//! > [`sp_runtime::traits::Applyable`]'s implementation.
|
||||
//!
|
||||
//! ## Asserting on a Custom Internal Origin
|
||||
//!
|
||||
//! In order to assert on a custom origin that is defined within your pallet, we need a way to first
|
||||
//! convert the `<T as frame_system::Config>::RuntimeOrigin` into the local `enum Origin` of the
|
||||
//! current pallet. This is a common process that is explained in
|
||||
//! [`crate::reference_docs::frame_runtime_types#
|
||||
//! adding-further-constraints-to-runtime-composite-enums`].
|
||||
//!
|
||||
//! 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 pallet.
|
||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin_bound)]
|
||||
//!
|
||||
//! 2. Using it in the pallet.
|
||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", custom_origin_usage)]
|
||||
//!
|
||||
//! ## Asserting on a Custom External Origin
|
||||
//!
|
||||
//! Very often, a pallet wants to have a parameterized origin that is **NOT** defined within the
|
||||
//! pallet. In other words, a pallet 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`.
|
||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_def)]
|
||||
//!
|
||||
//! Then, within the pallet, we can simply use this "unknown" origin check type:
|
||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_usage)]
|
||||
//!
|
||||
//! Finally, at the runtime, any implementation of [`frame::traits::EnsureOrigin`] can be passed.
|
||||
#![doc = docify::embed!("./src/reference_docs/frame_origin.rs", external_origin_provide)]
|
||||
//!
|
||||
//! Indeed, some of these implementations of [`frame::traits::EnsureOrigin`] are similar to the ones
|
||||
//! that we know about: [`frame::runtime::prelude::EnsureSigned`],
|
||||
//! [`frame::runtime::prelude::EnsureSignedBy`], [`frame::runtime::prelude::EnsureRoot`],
|
||||
//! [`frame::runtime::prelude::EnsureNone`], etc. But, there are also many more that are not known
|
||||
//! to us, and are defined in other pallets.
|
||||
//!
|
||||
//! For example, [`pallet_collective`] defines [`pallet_collective::EnsureMember`] and
|
||||
//! [`pallet_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`](frame::traits::EnsureOrigin#implementors) 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 pallet 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.](https://youtu.be/j7b8Upipmeg?si=83_XUgYuJxMwWX4g&t=195)
|
||||
//! - [A related StackExchange question.](https://substrate.stackexchange.com/questions/10992/how-do-you-find-the-public-key-for-the-medium-spender-track-origin)
|
||||
//!
|
||||
//! [^1]: Inherents are essentially unsigned extrinsics that need an [`frame_system::ensure_none`]
|
||||
//! origin check, and through the virtue of being an inherent, are agreed upon by all validators.
|
||||
|
||||
use frame::prelude::*;
|
||||
|
||||
#[frame::pallet(dev_mode)]
|
||||
pub mod pallet_for_origin {
|
||||
use super::*;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
#[docify::export(call_simple)]
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
pub fn do_something(_origin: OriginFor<T>) -> DispatchResult {
|
||||
// ^^^^^^^^^^^^^^^^^^^^^
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[frame::pallet(dev_mode)]
|
||||
pub mod pallet_with_custom_origin {
|
||||
use super::*;
|
||||
|
||||
#[docify::export(custom_origin_bound)]
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
type RuntimeOrigin: From<<Self as frame_system::Config>::RuntimeOrigin>
|
||||
+ Into<Result<Origin, <Self as Config>::RuntimeOrigin>>;
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
#[docify::export(custom_origin)]
|
||||
/// A dummy custom origin.
|
||||
#[pallet::origin]
|
||||
#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, 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,
|
||||
}
|
||||
|
||||
#[docify::export(custom_origin_usage)]
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
pub fn only_validators(origin: OriginFor<T>) -> DispatchResult {
|
||||
// first, we convert from `<T as frame_system::Config>::RuntimeOrigin` to `<T as
|
||||
// Config>::RuntimeOrigin`
|
||||
let local_runtime_origin = <<T as Config>::RuntimeOrigin as From<
|
||||
<T as frame_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!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod runtime_for_origin {
|
||||
use super::pallet_with_custom_origin;
|
||||
use frame::{runtime::prelude::*, testing_prelude::*};
|
||||
|
||||
#[docify::export(runtime_exp)]
|
||||
construct_runtime!(
|
||||
pub struct Runtime {
|
||||
System: frame_system,
|
||||
PalletWithCustomOrigin: pallet_with_custom_origin,
|
||||
}
|
||||
);
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
|
||||
impl frame_system::Config for Runtime {
|
||||
type Block = MockBlock<Self>;
|
||||
}
|
||||
|
||||
impl pallet_with_custom_origin::Config for Runtime {
|
||||
type RuntimeOrigin = RuntimeOrigin;
|
||||
}
|
||||
}
|
||||
|
||||
#[frame::pallet(dev_mode)]
|
||||
pub mod pallet_with_external_origin {
|
||||
use super::*;
|
||||
#[docify::export(external_origin_def)]
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
type ExternalOrigin: EnsureOrigin<Self::RuntimeOrigin>;
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
#[docify::export(external_origin_usage)]
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
pub fn externally_checked_ext(origin: OriginFor<T>) -> DispatchResult {
|
||||
let _ = T::ExternalOrigin::ensure_origin(origin)?;
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod runtime_for_external_origin {
|
||||
use super::*;
|
||||
use frame::{runtime::prelude::*, testing_prelude::*};
|
||||
|
||||
construct_runtime!(
|
||||
pub struct Runtime {
|
||||
System: frame_system,
|
||||
PalletWithExternalOrigin: pallet_with_external_origin,
|
||||
}
|
||||
);
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
|
||||
impl frame_system::Config for Runtime {
|
||||
type Block = MockBlock<Self>;
|
||||
}
|
||||
|
||||
#[docify::export(external_origin_provide)]
|
||||
impl pallet_with_external_origin::Config for Runtime {
|
||||
type ExternalOrigin = EnsureSigned<<Self as frame_system::Config>::AccountId>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
//! # FRAME Runtime Types
|
||||
//!
|
||||
//! This reference document briefly explores the idea around types generated at the runtime level by
|
||||
//! the FRAME macros.
|
||||
//!
|
||||
//! > As of now, many of these important types are generated within the internals of
|
||||
//! > [`construct_runtime`], and there is no easy way for you to visually know they exist.
|
||||
//! > [#polkadot-sdk#1378](https://github.com/paritytech/polkadot-sdk/pull/1378) is meant to
|
||||
//! > significantly improve this. Exploring the rust-docs of a runtime, such as [`runtime`] which is
|
||||
//! > defined in this module is as of now the best way to learn about these types.
|
||||
//!
|
||||
//! ## Composite Enums
|
||||
//!
|
||||
//! Many types within a FRAME runtime follow the following structure:
|
||||
//!
|
||||
//! * Each individual pallet defines a type, for example `Foo`.
|
||||
//! * At the runtime level, these types are amalgamated into a single type, for example
|
||||
//! `RuntimeFoo`.
|
||||
//!
|
||||
//! As the names suggest, all composite enums in a FRAME runtime start their name with `Runtime`.
|
||||
//! For example, `RuntimeCall` is a representation of the most high level `Call`-able type in the
|
||||
//! runtime.
|
||||
//!
|
||||
//! Composite enums are generally convertible to their individual parts as such:
|
||||
#![doc = simple_mermaid::mermaid!("../../../mermaid/outer_runtime_types.mmd")]
|
||||
//!
|
||||
//! In that one can always convert from the inner type into the outer type, but not vice versa. This
|
||||
//! is usually expressed by implementing `From`, `TryFrom`, `From<Result<_>>` and similar traits.
|
||||
//!
|
||||
//! ### Example
|
||||
//!
|
||||
//! We provide the following two pallets: [`pallet_foo`] and [`pallet_bar`]. Each define a
|
||||
//! dispatchable, and `Foo` also defines a custom origin. Lastly, `Bar` defines an additional
|
||||
//! `GenesisConfig`.
|
||||
#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_foo)]
|
||||
#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_bar)]
|
||||
//!
|
||||
//! Let's explore how each of these affect the [`RuntimeCall`], [`RuntimeOrigin`] and
|
||||
//! [`RuntimeGenesisConfig`] generated in [`runtime`] by respectively.
|
||||
//!
|
||||
//! As observed, [`RuntimeCall`] has 3 variants, one for each pallet and one for `frame_system`. If
|
||||
//! you explore further, you will soon realize that each variant is merely a pointer to the `Call`
|
||||
//! type in each pallet, for example [`pallet_foo::Call`].
|
||||
//!
|
||||
//! [`RuntimeOrigin`]'s [`OriginCaller`] has two variants, one for system, and one for `pallet_foo`
|
||||
//! which utilized [`frame::pallet_macros::origin`].
|
||||
//!
|
||||
//! Finally, [`RuntimeGenesisConfig`] is composed of `frame_system` and a variant for `pallet_bar`'s
|
||||
//! [`pallet_bar::GenesisConfig`].
|
||||
//!
|
||||
//! You can find other composite enums by scanning [`runtime`] for other types who's name starts
|
||||
//! with `Runtime`. Some of the more noteworthy ones are:
|
||||
//!
|
||||
//! - [`RuntimeEvent`]
|
||||
//! - [`RuntimeError`]
|
||||
//! - [`RuntimeHoldReason`]
|
||||
//!
|
||||
//! ### Adding Further Constraints to Runtime Composite Enums
|
||||
//!
|
||||
//! This section explores a common scenario where a pallet has access to one of these runtime
|
||||
//! composite enums, but it wishes to further specify it by adding more trait bounds to it.
|
||||
//!
|
||||
//! Let's take the example of `RuntimeCall`. This is an associated type in
|
||||
//! [`frame_system::Config::RuntimeCall`], and all pallets have access to this type, because they
|
||||
//! have access to [`frame_system::Config`]. Finally, this type is meant to be set to outer call of
|
||||
//! the entire runtime.
|
||||
//!
|
||||
//! But, let's not forget that this is information that *we know*, and the Rust compiler does not.
|
||||
//! All that the rust compiler knows about this type is *ONLY* what the trait bounds of
|
||||
//! [`frame_system::Config::RuntimeCall`] are specifying:
|
||||
#![doc = docify::embed!("../../substrate/frame/system/src/lib.rs", system_runtime_call)]
|
||||
//!
|
||||
//! So, when at a given pallet, one accesses `<T as frame_system::Config>::RuntimeCall`, the type is
|
||||
//! extremely opaque from the perspective of the Rust compiler.
|
||||
//!
|
||||
//! How can a pallet access the `RuntimeCall` type with further constraints? For example, each
|
||||
//! pallet has its own `enum Call`, and knows that its local `Call` is a part of `RuntimeCall`,
|
||||
//! therefore there should be a `impl From<Call<_>> for RuntimeCall`.
|
||||
//!
|
||||
//! The only way to express this using Rust's associated types is for the pallet to **define its own
|
||||
//! associated type `RuntimeCall`, and further specify what it thinks `RuntimeCall` should be**.
|
||||
//!
|
||||
//! In this case, we will want to assert the existence of [`frame::traits::IsSubType`], which is
|
||||
//! very similar to [`TryFrom`].
|
||||
#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", custom_runtime_call)]
|
||||
//!
|
||||
//! And indeed, at the runtime level, this associated type would be the same `RuntimeCall` that is
|
||||
//! passed to `frame_system`.
|
||||
#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", pallet_with_specific_runtime_call_impl)]
|
||||
//!
|
||||
//! > In other words, the degree of specificity that [`frame_system::Config::RuntimeCall`] has is
|
||||
//! > not enough for the pallet to work with. Therefore, the pallet has to define its own associated
|
||||
//! > type representing `RuntimeCall`.
|
||||
//!
|
||||
//! Another way to look at this is:
|
||||
//!
|
||||
//! `pallet_with_specific_runtime_call::Config::RuntimeCall` and `frame_system::Config::RuntimeCall`
|
||||
//! are two different representations of the same concrete type that is only known when the runtime
|
||||
//! is being constructed.
|
||||
//!
|
||||
//! Now, within this pallet, this new `RuntimeCall` can be used, and it can use its new trait
|
||||
//! bounds, such as being [`frame::traits::IsSubType`]:
|
||||
#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", custom_runtime_call_usages)]
|
||||
//!
|
||||
//! ### Asserting Equality of Multiple Runtime Composite Enums
|
||||
//!
|
||||
//! Recall that in the above example, `<T as Config>::RuntimeCall` and `<T as
|
||||
//! frame_system::Config>::RuntimeCall` are expected to be equal types, but at the compile-time we
|
||||
//! have to represent them with two different associated types with different bounds. Would it not
|
||||
//! be cool if we had a test to make sure they actually resolve to the same concrete type once the
|
||||
//! runtime is constructed? The following snippet exactly does that:
|
||||
#![doc = docify::embed!("./src/reference_docs/frame_runtime_types.rs", assert_equality)]
|
||||
//!
|
||||
//! We leave it to the reader to further explore what [`frame::traits::Hooks::integrity_test`] is,
|
||||
//! and what [`core::any::TypeId`] is. Another way to assert this is using
|
||||
//! [`frame::traits::IsType`].
|
||||
//!
|
||||
//! ## Type Aliases
|
||||
//!
|
||||
//! A number of type aliases are generated by the `construct_runtime` which are also noteworthy:
|
||||
//!
|
||||
//! * [`runtime::PalletFoo`] is an alias to [`pallet_foo::Pallet`]. Same for `PalletBar`, and
|
||||
//! `System`
|
||||
//! * [`runtime::AllPalletsWithSystem`] is an alias for a tuple of all of the above. This type is
|
||||
//! important to FRAME internals such as `executive`, as it implements traits such as
|
||||
//! [`frame::traits::Hooks`].
|
||||
//!
|
||||
//! ## Further Details
|
||||
//!
|
||||
//! * [`crate::reference_docs::frame_origin`] explores further details about the usage of
|
||||
//! `RuntimeOrigin`.
|
||||
//! * [`RuntimeCall`] is a particularly interesting composite enum as it dictates the encoding of an
|
||||
//! extrinsic. See [`crate::reference_docs::signed_extensions`] for more information.
|
||||
//! * See the documentation of [`construct_runtime`].
|
||||
//! * See the corresponding lecture in the [pba-book](https://polkadot-blockchain-academy.github.io/pba-book/frame/outer-enum/page.html).
|
||||
//!
|
||||
//!
|
||||
//! [`construct_runtime`]: frame::runtime::prelude::construct_runtime
|
||||
//! [`runtime::PalletFoo`]: crate::reference_docs::frame_runtime_types::runtime::PalletFoo
|
||||
//! [`runtime::AllPalletsWithSystem`]: crate::reference_docs::frame_runtime_types::runtime::AllPalletsWithSystem
|
||||
//! [`runtime`]: crate::reference_docs::frame_runtime_types::runtime
|
||||
//! [`pallet_foo`]: crate::reference_docs::frame_runtime_types::pallet_foo
|
||||
//! [`pallet_foo::Call`]: crate::reference_docs::frame_runtime_types::pallet_foo::Call
|
||||
//! [`pallet_foo::Pallet`]: crate::reference_docs::frame_runtime_types::pallet_foo::Pallet
|
||||
//! [`pallet_bar`]: crate::reference_docs::frame_runtime_types::pallet_bar
|
||||
//! [`pallet_bar::GenesisConfig`]: crate::reference_docs::frame_runtime_types::pallet_bar::GenesisConfig
|
||||
//! [`RuntimeEvent`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeEvent
|
||||
//! [`RuntimeGenesisConfig`]:
|
||||
//! crate::reference_docs::frame_runtime_types::runtime::RuntimeGenesisConfig
|
||||
//! [`RuntimeOrigin`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeOrigin
|
||||
//! [`OriginCaller`]: crate::reference_docs::frame_runtime_types::runtime::OriginCaller
|
||||
//! [`RuntimeError`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeError
|
||||
//! [`RuntimeCall`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeCall
|
||||
//! [`RuntimeHoldReason`]: crate::reference_docs::frame_runtime_types::runtime::RuntimeHoldReason
|
||||
|
||||
use frame::prelude::*;
|
||||
|
||||
#[docify::export]
|
||||
#[frame::pallet(dev_mode)]
|
||||
pub mod pallet_foo {
|
||||
use super::*;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::origin]
|
||||
#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||
pub enum Origin {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
pub fn foo(_origin: OriginFor<T>) -> DispatchResult {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub fn other(_origin: OriginFor<T>) -> DispatchResult {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
#[frame::pallet(dev_mode)]
|
||||
pub mod pallet_bar {
|
||||
use super::*;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
#[pallet::genesis_config]
|
||||
#[derive(DefaultNoBound)]
|
||||
pub struct GenesisConfig<T: Config> {
|
||||
pub initial_account: Option<T::AccountId>,
|
||||
}
|
||||
|
||||
#[pallet::genesis_build]
|
||||
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
|
||||
fn build(&self) {}
|
||||
}
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
pub fn bar(_origin: OriginFor<T>) -> DispatchResult {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod runtime {
|
||||
use super::{pallet_bar, pallet_foo};
|
||||
use frame::{runtime::prelude::*, testing_prelude::*};
|
||||
|
||||
#[docify::export(runtime_exp)]
|
||||
construct_runtime!(
|
||||
pub struct Runtime {
|
||||
System: frame_system,
|
||||
PalletFoo: pallet_foo,
|
||||
PalletBar: pallet_bar,
|
||||
}
|
||||
);
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
|
||||
impl frame_system::Config for Runtime {
|
||||
type Block = MockBlock<Self>;
|
||||
}
|
||||
|
||||
impl pallet_foo::Config for Runtime {}
|
||||
impl pallet_bar::Config for Runtime {}
|
||||
}
|
||||
|
||||
#[frame::pallet(dev_mode)]
|
||||
pub mod pallet_with_specific_runtime_call {
|
||||
use super::*;
|
||||
use frame::traits::IsSubType;
|
||||
|
||||
#[docify::export(custom_runtime_call)]
|
||||
/// A pallet that wants to further narrow down what `RuntimeCall` is.
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
type RuntimeCall: IsSubType<Call<Self>>;
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
// note that this pallet needs some `call` to have a `enum Call`.
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
pub fn foo(_origin: OriginFor<T>) -> DispatchResult {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
#[docify::export(custom_runtime_call_usages)]
|
||||
impl<T: Config> Pallet<T> {
|
||||
fn _do_something_useful_with_runtime_call(call: <T as Config>::RuntimeCall) {
|
||||
// check if the runtime call given is of this pallet's variant.
|
||||
let _maybe_my_call: Option<&Call<T>> = call.is_sub_type();
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
#[docify::export(assert_equality)]
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
||||
fn integrity_test() {
|
||||
use core::any::TypeId;
|
||||
assert_eq!(
|
||||
TypeId::of::<<T as Config>::RuntimeCall>(),
|
||||
TypeId::of::<<T as frame_system::Config>::RuntimeCall>()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod runtime_with_specific_runtime_call {
|
||||
use super::pallet_with_specific_runtime_call;
|
||||
use frame::{runtime::prelude::*, testing_prelude::*};
|
||||
|
||||
construct_runtime!(
|
||||
pub struct Runtime {
|
||||
System: frame_system,
|
||||
PalletWithSpecificRuntimeCall: pallet_with_specific_runtime_call,
|
||||
}
|
||||
);
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
|
||||
impl frame_system::Config for Runtime {
|
||||
type Block = MockBlock<Self>;
|
||||
}
|
||||
|
||||
#[docify::export(pallet_with_specific_runtime_call_impl)]
|
||||
impl pallet_with_specific_runtime_call::Config for Runtime {
|
||||
// an implementation of `IsSubType` is provided by `construct_runtime`.
|
||||
type RuntimeCall = RuntimeCall;
|
||||
}
|
||||
}
|
||||
@@ -43,16 +43,16 @@ pub mod extrinsic_encoding;
|
||||
// TODO: @jsdw https://github.com/paritytech/polkadot-sdk-docs/issues/42
|
||||
pub mod signed_extensions;
|
||||
|
||||
/// Learn about *"Origin"* A topic in FRAME that enables complex account abstractions to be built.
|
||||
// TODO: @shawntabrizi https://github.com/paritytech/polkadot-sdk-docs/issues/43
|
||||
/// Learn about *Origins*, a topic in FRAME that enables complex account abstractions to be built.
|
||||
pub mod frame_origin;
|
||||
|
||||
/// Learn about how to write safe and defensive code in your FRAME runtime.
|
||||
// TODO: @CrackTheCode016 https://github.com/paritytech/polkadot-sdk-docs/issues/44
|
||||
pub mod safe_defensive_programming;
|
||||
|
||||
/// Learn about composite enums in FRAME-based runtimes, such as "RuntimeEvent" and "RuntimeCall".
|
||||
pub mod frame_composite_enums;
|
||||
/// Learn about composite enums and other runtime level types, such as "RuntimeEvent" and
|
||||
/// "RuntimeCall".
|
||||
pub mod frame_runtime_types;
|
||||
|
||||
/// Learn about how to make a pallet/runtime that is fee-less and instead uses another mechanism to
|
||||
/// control usage and sybil attacks.
|
||||
|
||||
@@ -163,6 +163,12 @@ pub mod runtime {
|
||||
ConstU32, ConstU64, ConstU8,
|
||||
};
|
||||
|
||||
/// Primary types used to parameterize `EnsureOrigin` and `EnsureRootWithArg`.
|
||||
pub use frame_system::{
|
||||
EnsureNever, EnsureNone, EnsureRoot, EnsureRootWithSuccess, EnsureSigned,
|
||||
EnsureSignedBy,
|
||||
};
|
||||
|
||||
/// Types to define your runtime version.
|
||||
pub use sp_version::{create_runtime_str, runtime_version, RuntimeVersion};
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ pub fn expand_outer_origin(
|
||||
#[doc = #doc_string]
|
||||
#[derive(Clone)]
|
||||
pub struct RuntimeOrigin {
|
||||
caller: OriginCaller,
|
||||
pub caller: OriginCaller,
|
||||
filter: #scrate::__private::sp_std::rc::Rc<Box<dyn Fn(&<#runtime as #system_path::Config>::RuntimeCall) -> bool>>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1480,22 +1480,11 @@ pub fn validate_unsigned(_: TokenStream, _: TokenStream) -> TokenStream {
|
||||
pallet_macro_stub()
|
||||
}
|
||||
|
||||
/// The `#[pallet::origin]` attribute allows you to define some origin for the pallet.
|
||||
///
|
||||
/// Item must be either a type alias, an enum, or a struct. It needs to be public.
|
||||
/// ---
|
||||
///
|
||||
/// E.g.:
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[pallet::origin]
|
||||
/// pub struct Origin<T>(PhantomData<(T)>);
|
||||
/// ```
|
||||
///
|
||||
/// **WARNING**: modifying origin changes the outer runtime origin. This outer runtime origin
|
||||
/// can be stored on-chain (e.g. in `pallet-scheduler`), thus any change must be done with care
|
||||
/// as it might require some migration.
|
||||
///
|
||||
/// NOTE: for instantiable pallets, the origin must be generic over `T` and `I`.
|
||||
/// **Rust-Analyzer users**: See the documentation of the Rust item in
|
||||
/// `frame_support::pallet_macros::origin`.
|
||||
#[proc_macro_attribute]
|
||||
pub fn origin(_: TokenStream, _: TokenStream) -> TokenStream {
|
||||
pallet_macro_stub()
|
||||
|
||||
@@ -2274,9 +2274,8 @@ pub mod pallet_macros {
|
||||
pub use frame_support_procedural::{
|
||||
composite_enum, config, disable_frame_system_supertrait_check, error, event,
|
||||
extra_constants, feeless_if, generate_deposit, generate_store, getter, hooks,
|
||||
import_section, inherent, no_default, no_default_bounds, origin, pallet_section,
|
||||
storage_prefix, storage_version, type_value, unbounded, validate_unsigned, weight,
|
||||
whitelist_storage,
|
||||
import_section, inherent, no_default, no_default_bounds, pallet_section, storage_prefix,
|
||||
storage_version, type_value, unbounded, validate_unsigned, weight, whitelist_storage,
|
||||
};
|
||||
|
||||
/// Allows a pallet to declare a set of functions as a *dispatchable extrinsic*. In
|
||||
@@ -2718,7 +2717,7 @@ pub mod pallet_macros {
|
||||
/// }
|
||||
/// ```
|
||||
pub use frame_support_procedural::storage;
|
||||
/// This attribute is attached to a function inside an `impl` block annoated with
|
||||
/// This attribute is attached to a function inside an `impl` block annotated with
|
||||
/// [`pallet::tasks_experimental`](`tasks_experimental`) to define the conditions for a
|
||||
/// given work item to be valid.
|
||||
///
|
||||
@@ -2726,21 +2725,21 @@ pub mod pallet_macros {
|
||||
/// should have the same signature as the function it is attached to, except that it should
|
||||
/// return a `bool` instead.
|
||||
pub use frame_support_procedural::task_condition;
|
||||
/// This attribute is attached to a function inside an `impl` block annoated with
|
||||
/// This attribute is attached to a function inside an `impl` block annotated with
|
||||
/// [`pallet::tasks_experimental`](`tasks_experimental`) to define the index of a given
|
||||
/// work item.
|
||||
///
|
||||
/// It takes an integer literal as input, which is then used to define the index. This
|
||||
/// index should be unique for each function in the `impl` block.
|
||||
pub use frame_support_procedural::task_index;
|
||||
/// This attribute is attached to a function inside an `impl` block annoated with
|
||||
/// This attribute is attached to a function inside an `impl` block annotated with
|
||||
/// [`pallet::tasks_experimental`](`tasks_experimental`) to define an iterator over the
|
||||
/// available work items for a task.
|
||||
///
|
||||
/// It takes an iterator as input that yields a tuple with same types as the function
|
||||
/// arguments.
|
||||
pub use frame_support_procedural::task_list;
|
||||
/// This attribute is attached to a function inside an `impl` block annoated with
|
||||
/// This attribute is attached to a function inside an `impl` block annotated with
|
||||
/// [`pallet::tasks_experimental`](`tasks_experimental`) define the weight of a given work
|
||||
/// item.
|
||||
///
|
||||
@@ -2773,6 +2772,61 @@ pub mod pallet_macros {
|
||||
/// Now, this can be executed as follows:
|
||||
#[doc = docify::embed!("src/tests/tasks.rs", tasks_work)]
|
||||
pub use frame_support_procedural::tasks_experimental;
|
||||
|
||||
/// Allows a pallet to declare a type as an origin.
|
||||
///
|
||||
/// If defined as such, this type will be amalgamated at the runtime level into
|
||||
/// `RuntimeOrigin`, very similar to [`call`], [`error`] and [`event`]. See
|
||||
/// [`composite_enum`] for similar cases.
|
||||
///
|
||||
/// Origin is a complex FRAME topics and is further explained in `polkadot_sdk_docs`.
|
||||
///
|
||||
/// ## Syntax Variants
|
||||
///
|
||||
/// ```
|
||||
/// #[frame_support::pallet]
|
||||
/// mod pallet {
|
||||
/// # use frame_support::pallet_prelude::*;
|
||||
/// # #[pallet::config]
|
||||
/// # pub trait Config: frame_system::Config {}
|
||||
/// # #[pallet::pallet]
|
||||
/// # pub struct Pallet<T>(_);
|
||||
/// /// On the spot declaration.
|
||||
/// #[pallet::origin]
|
||||
/// #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||
/// pub enum Origin {
|
||||
/// Foo,
|
||||
/// Bar,
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Or, more commonly used:/
|
||||
///
|
||||
/// ```
|
||||
/// #[frame_support::pallet]
|
||||
/// mod pallet {
|
||||
/// # use frame_support::pallet_prelude::*;
|
||||
/// # #[pallet::config]
|
||||
/// # pub trait Config: frame_system::Config {}
|
||||
/// # #[pallet::pallet]
|
||||
/// # pub struct Pallet<T>(_);
|
||||
/// #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||
/// pub enum RawOrigin {
|
||||
/// Foo,
|
||||
/// Bar,
|
||||
/// }
|
||||
///
|
||||
/// #[pallet::origin]
|
||||
/// pub type Origin = RawOrigin;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Warning
|
||||
///
|
||||
/// Modifying any pallet's origin type will cause the runtime level origin type to also
|
||||
/// change in encoding. If stored anywhere on-chain, this will require a data migration.
|
||||
pub use frame_support_procedural::origin;
|
||||
}
|
||||
|
||||
#[deprecated(note = "Will be removed after July 2023; Use `sp_runtime::traits` directly instead.")]
|
||||
|
||||
@@ -452,6 +452,7 @@ pub mod pallet {
|
||||
+ Clone
|
||||
+ OriginTrait<Call = Self::RuntimeCall, AccountId = Self::AccountId>;
|
||||
|
||||
#[docify::export(system_runtime_call)]
|
||||
/// The aggregated `RuntimeCall` type.
|
||||
#[pallet::no_default_bounds]
|
||||
type RuntimeCall: Parameter
|
||||
|
||||
Reference in New Issue
Block a user