mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-08-09 13:25:45 +00:00
82f3c3e2e8
Moved from https://github.com/paritytech/substrate/pull/14788 ---- Fixes https://github.com/paritytech/polkadot-sdk/issues/232 This PR introduces outer-macro approach for `construct_runtime` as discussed in the linked issue. It looks like the following: ```rust #[frame_support::runtime] mod runtime { #[runtime::runtime] #[runtime::derive( RuntimeCall, RuntimeEvent, RuntimeError, RuntimeOrigin, RuntimeFreezeReason, RuntimeHoldReason, RuntimeSlashReason, RuntimeLockId, RuntimeTask, )] pub struct Runtime; #[runtime::pallet_index(0)] pub type System = frame_system; #[runtime::pallet_index(1)] pub type Timestamp = pallet_timestamp; #[runtime::pallet_index(2)] pub type Aura = pallet_aura; #[runtime::pallet_index(3)] pub type Grandpa = pallet_grandpa; #[runtime::pallet_index(4)] pub type Balances = pallet_balances; #[runtime::pallet_index(5)] pub type TransactionPayment = pallet_transaction_payment; #[runtime::pallet_index(6)] pub type Sudo = pallet_sudo; // Include the custom logic from the pallet-template in the runtime. #[runtime::pallet_index(7)] pub type TemplateModule = pallet_template; } ``` ## Features - `#[runtime::runtime]` attached to a struct defines the main runtime - `#[runtime::derive]` attached to this struct defines the types generated by runtime - `#[runtime::pallet_index]` must be attached to a pallet to define its index - `#[runtime::disable_call]` can be optionally attached to a pallet to disable its calls - `#[runtime::disable_unsigned]` can be optionally attached to a pallet to disable unsigned calls - A pallet instance can be defined as `TemplateModule: pallet_template<Instance>` - An optional attribute can be defined as `#[frame_support::runtime(legacy_ordering)]` to ensure that the order of hooks is same as the order of pallets (and not based on the pallet_index). This is to support legacy runtimes and should be avoided for new ones. ## Todo - [x] Update the latest syntax in kitchensink and tests - [x] Update UI tests - [x] Docs ## Extension - Abstract away the Executive similar to https://github.com/paritytech/substrate/pull/14742 - Optionally avoid the need to specify all runtime types (TBD) --------- Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> Co-authored-by: Nikhil Gupta <>
237 lines
8.3 KiB
Rust
237 lines
8.3 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! Implementation of `runtime`.
|
|
//!
|
|
//! `runtime` implementation is recursive and can generate code which will call itself
|
|
//! in order to get all the pallet parts for each pallet.
|
|
//!
|
|
//! Pallets can define their parts:
|
|
//! - Implicitly: `pub type System = frame_system;`
|
|
//! - Explicitly: `pub type System = frame_system + Pallet + Call;`
|
|
//!
|
|
//! The `runtime` transitions from the implicit definition to the explicit one.
|
|
//! From the explicit state, Substrate expands the pallets with additional information
|
|
//! that is to be included in the runtime metadata.
|
|
//!
|
|
//! Pallets must provide the `tt_default_parts_v2` macro for these transitions.
|
|
//! These are automatically implemented by the `#[pallet::pallet]` macro.
|
|
//!
|
|
//! This macro also generates the following enums for ease of decoding if the respective type
|
|
//! is defined inside `#[runtime::derive]`:
|
|
//! - `enum RuntimeCall`: This type contains the information needed to decode extrinsics.
|
|
//! - `enum RuntimeEvent`: This type contains the information needed to decode events.
|
|
//! - `enum RuntimeError`: While this cannot be used directly to decode `sp_runtime::DispatchError`
|
|
//! from the chain, it contains the information needed to decode the
|
|
//! `sp_runtime::DispatchError::Module`.
|
|
//!
|
|
//! # State Transitions
|
|
//!
|
|
//! ```ignore
|
|
//! +----------+
|
|
//! | Implicit |
|
|
//! +----------+
|
|
//! |
|
|
//! v
|
|
//! +----------+
|
|
//! | Explicit |
|
|
//! +----------+
|
|
//! ```
|
|
//!
|
|
//! The `runtime` macro transforms the implicit declaration of each pallet
|
|
//! `System: frame_system` to an explicit one `System: frame_system + Pallet + Call` using the
|
|
//! `tt_default_parts_v2` macro.
|
|
//!
|
|
//! The `tt_default_parts_v2` macro exposes a plus separated list of pallet parts. For example, the
|
|
//! `Event` part is exposed only if the pallet implements an event via `#[pallet::event]` macro.
|
|
//! The tokens generated by this macro are `+ Pallet + Call` for our example.
|
|
//!
|
|
//! The `match_and_insert` macro takes in 3 arguments:
|
|
//! - target: This is the `TokenStream` that contains the `runtime` macro.
|
|
//! - pattern: The pattern to match against in the target stream.
|
|
//! - tokens: The tokens to added after the pattern match.
|
|
//!
|
|
//! The `runtime` macro uses the `tt_call` to get the default pallet parts via
|
|
//! the `tt_default_parts_v2` macro defined by each pallet. The pallet parts are then returned as
|
|
//! input to the `match_and_replace` macro.
|
|
//! The `match_and_replace` then will modify the `runtime` to expand the implicit
|
|
//! definition to the explicit one.
|
|
//!
|
|
//! For example,
|
|
//!
|
|
//! ```ignore
|
|
//! #[frame_support::runtime]
|
|
//! mod runtime {
|
|
//! //...
|
|
//!
|
|
//! #[runtime::pallet_index(0)]
|
|
//! pub type System = frame_system; // Implicit definition of parts
|
|
//!
|
|
//! #[runtime::pallet_index(1)]
|
|
//! pub type Balances = pallet_balances; // Implicit definition of parts
|
|
//! }
|
|
//! ```
|
|
//! This call has some implicit pallet parts, thus it will expand to:
|
|
//! ```ignore
|
|
//! frame_support::__private::tt_call! {
|
|
//! macro = [{ pallet_balances::tt_default_parts_v2 }]
|
|
//! ~~> frame_support::match_and_insert! {
|
|
//! target = [{
|
|
//! frame_support::__private::tt_call! {
|
|
//! macro = [{ frame_system::tt_default_parts_v2 }]
|
|
//! ~~> frame_support::match_and_insert! {
|
|
//! target = [{
|
|
//! #[frame_support::runtime]
|
|
//! mod runtime {
|
|
//! //...
|
|
//!
|
|
//! #[runtime::pallet_index(0)]
|
|
//! pub type System = frame_system;
|
|
//!
|
|
//! #[runtime::pallet_index(1)]
|
|
//! pub type Balances = pallet_balances;
|
|
//! }
|
|
//! }]
|
|
//! pattern = [{ System = frame_system }]
|
|
//! }
|
|
//! }
|
|
//! }]
|
|
//! pattern = [{ Balances = pallet_balances }]
|
|
//! }
|
|
//! }
|
|
//! ```
|
|
//! `tt_default_parts_v2` must be defined. It returns the pallet parts inside some tokens, and
|
|
//! then `tt_call` will pipe the returned pallet parts into the input of `match_and_insert`.
|
|
//! Thus `match_and_insert` will initially receive the following inputs:
|
|
//! ```ignore
|
|
//! frame_support::match_and_insert! {
|
|
//! target = [{
|
|
//! frame_support::match_and_insert! {
|
|
//! target = [{
|
|
//! #[frame_support::runtime]
|
|
//! mod runtime {
|
|
//! //...
|
|
//!
|
|
//! #[runtime::pallet_index(0)]
|
|
//! pub type System = frame_system;
|
|
//!
|
|
//! #[runtime::pallet_index(1)]
|
|
//! pub type Balances = pallet_balances;
|
|
//! }
|
|
//! }]
|
|
//! pattern = [{ System = frame_system }]
|
|
//! tokens = [{ ::{+ Pallet + Call} }]
|
|
//! }
|
|
//! }]
|
|
//! pattern = [{ Balances = pallet_balances }]
|
|
//! tokens = [{ ::{+ Pallet + Call} }]
|
|
//! }
|
|
//! ```
|
|
//! After dealing with `pallet_balances`, the inner `match_and_insert` will expand to:
|
|
//! ```ignore
|
|
//! frame_support::match_and_insert! {
|
|
//! target = [{
|
|
//! #[frame_support::runtime]
|
|
//! mod runtime {
|
|
//! //...
|
|
//!
|
|
//! #[runtime::pallet_index(0)]
|
|
//! pub type System = frame_system; // Implicit definition of parts
|
|
//!
|
|
//! #[runtime::pallet_index(1)]
|
|
//! pub type Balances = pallet_balances + Pallet + Call; // Explicit definition of parts
|
|
//! }
|
|
//! }]
|
|
//! pattern = [{ System = frame_system }]
|
|
//! tokens = [{ ::{+ Pallet + Call} }]
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! Which will then finally expand to the following:
|
|
//! ```ignore
|
|
//! #[frame_support::runtime]
|
|
//! mod runtime {
|
|
//! //...
|
|
//!
|
|
//! #[runtime::pallet_index(0)]
|
|
//! pub type System = frame_system + Pallet + Call;
|
|
//!
|
|
//! #[runtime::pallet_index(1)]
|
|
//! pub type Balances = pallet_balances + Pallet + Call;
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! This call has no implicit pallet parts, thus it will expand to the runtime construction:
|
|
//! ```ignore
|
|
//! pub struct Runtime { ... }
|
|
//! pub struct Call { ... }
|
|
//! impl Call ...
|
|
//! pub enum Origin { ... }
|
|
//! ...
|
|
//! ```
|
|
//!
|
|
//! Visualizing the entire flow of `#[frame_support::runtime]`, it would look like the following:
|
|
//!
|
|
//! ```ignore
|
|
//! +----------------------+ +------------------------+ +-------------------+
|
|
//! | | | (defined in pallet) | | |
|
|
//! | runtime | --> | tt_default_parts_v2! | --> | match_and_insert! |
|
|
//! | w/ no pallet parts | | | | |
|
|
//! +----------------------+ +------------------------+ +-------------------+
|
|
//!
|
|
//! +----------------------+
|
|
//! | |
|
|
//! --> | runtime |
|
|
//! | w/ pallet parts |
|
|
//! +----------------------+
|
|
//! ```
|
|
|
|
#![cfg(feature = "experimental")]
|
|
|
|
pub use parse::Def;
|
|
use proc_macro::TokenStream;
|
|
use syn::spanned::Spanned;
|
|
|
|
mod expand;
|
|
mod parse;
|
|
|
|
mod keyword {
|
|
syn::custom_keyword!(legacy_ordering);
|
|
}
|
|
|
|
pub fn runtime(attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
|
let mut legacy_ordering = false;
|
|
if !attr.is_empty() {
|
|
if let Ok(_) = syn::parse::<keyword::legacy_ordering>(attr.clone()) {
|
|
legacy_ordering = true;
|
|
} else {
|
|
let msg = "Invalid runtime macro call: unexpected attribute. Macro call must be \
|
|
bare, such as `#[frame_support::runtime]` or `#[runtime]`, or must specify the \
|
|
`legacy_ordering` attribute, such as `#[frame_support::runtime(legacy_ordering)]` or \
|
|
#[runtime(legacy_ordering)].";
|
|
let span = proc_macro2::TokenStream::from(attr).span();
|
|
return syn::Error::new(span, msg).to_compile_error().into()
|
|
}
|
|
}
|
|
|
|
let item = syn::parse_macro_input!(tokens as syn::ItemMod);
|
|
match parse::Def::try_from(item) {
|
|
Ok(def) => expand::expand(def, legacy_ordering).into(),
|
|
Err(e) => e.to_compile_error().into(),
|
|
}
|
|
}
|