mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-25 21:11:07 +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 <>
321 lines
9.3 KiB
Rust
321 lines
9.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.
|
|
|
|
use super::parse::runtime_types::RuntimeType;
|
|
use crate::{
|
|
construct_runtime::{
|
|
check_pallet_number, decl_all_pallets, decl_integrity_test, decl_pallet_runtime_setup,
|
|
decl_static_assertions, expand,
|
|
},
|
|
runtime::{
|
|
parse::{
|
|
AllPalletsDeclaration, ExplicitAllPalletsDeclaration, ImplicitAllPalletsDeclaration,
|
|
},
|
|
Def,
|
|
},
|
|
};
|
|
use cfg_expr::Predicate;
|
|
use frame_support_procedural_tools::{
|
|
generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes,
|
|
};
|
|
use proc_macro2::TokenStream as TokenStream2;
|
|
use quote::quote;
|
|
use std::collections::HashSet;
|
|
use syn::{Ident, Result};
|
|
|
|
/// The fixed name of the system pallet.
|
|
const SYSTEM_PALLET_NAME: &str = "System";
|
|
|
|
pub fn expand(def: Def, legacy_ordering: bool) -> TokenStream2 {
|
|
let input = def.input;
|
|
|
|
let (check_pallet_number_res, res) = match def.pallets {
|
|
AllPalletsDeclaration::Implicit(ref decl) => (
|
|
check_pallet_number(input.clone(), decl.pallet_count),
|
|
construct_runtime_implicit_to_explicit(input.into(), decl.clone(), legacy_ordering),
|
|
),
|
|
AllPalletsDeclaration::Explicit(ref decl) => (
|
|
check_pallet_number(input, decl.pallets.len()),
|
|
construct_runtime_final_expansion(
|
|
def.runtime_struct.ident.clone(),
|
|
decl.clone(),
|
|
def.runtime_types.clone(),
|
|
legacy_ordering,
|
|
),
|
|
),
|
|
};
|
|
|
|
let res = res.unwrap_or_else(|e| e.to_compile_error());
|
|
|
|
// We want to provide better error messages to the user and thus, handle the error here
|
|
// separately. If there is an error, we print the error and still generate all of the code to
|
|
// get in overall less errors for the user.
|
|
let res = if let Err(error) = check_pallet_number_res {
|
|
let error = error.to_compile_error();
|
|
|
|
quote! {
|
|
#error
|
|
|
|
#res
|
|
}
|
|
} else {
|
|
res
|
|
};
|
|
|
|
let res = expander::Expander::new("construct_runtime")
|
|
.dry(std::env::var("FRAME_EXPAND").is_err())
|
|
.verbose(true)
|
|
.write_to_out_dir(res)
|
|
.expect("Does not fail because of IO in OUT_DIR; qed");
|
|
|
|
res.into()
|
|
}
|
|
|
|
fn construct_runtime_implicit_to_explicit(
|
|
input: TokenStream2,
|
|
definition: ImplicitAllPalletsDeclaration,
|
|
legacy_ordering: bool,
|
|
) -> Result<TokenStream2> {
|
|
let frame_support = generate_access_from_frame_or_crate("frame-support")?;
|
|
let attr = if legacy_ordering { quote!((legacy_ordering)) } else { quote!() };
|
|
let mut expansion = quote::quote!(
|
|
#[frame_support::runtime #attr]
|
|
#input
|
|
);
|
|
for pallet in definition.pallet_decls.iter() {
|
|
let pallet_path = &pallet.path;
|
|
let pallet_name = &pallet.name;
|
|
let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(<#instance>));
|
|
expansion = quote::quote!(
|
|
#frame_support::__private::tt_call! {
|
|
macro = [{ #pallet_path::tt_default_parts_v2 }]
|
|
frame_support = [{ #frame_support }]
|
|
~~> #frame_support::match_and_insert! {
|
|
target = [{ #expansion }]
|
|
pattern = [{ #pallet_name = #pallet_path #pallet_instance }]
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
Ok(expansion)
|
|
}
|
|
|
|
fn construct_runtime_final_expansion(
|
|
name: Ident,
|
|
definition: ExplicitAllPalletsDeclaration,
|
|
runtime_types: Vec<RuntimeType>,
|
|
legacy_ordering: bool,
|
|
) -> Result<TokenStream2> {
|
|
let ExplicitAllPalletsDeclaration { mut pallets, name: pallets_name } = definition;
|
|
|
|
if !legacy_ordering {
|
|
// Ensure that order of hooks is based on the pallet index
|
|
pallets.sort_by_key(|p| p.index);
|
|
}
|
|
|
|
let system_pallet =
|
|
pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| {
|
|
syn::Error::new(
|
|
pallets_name.span(),
|
|
"`System` pallet declaration is missing. \
|
|
Please add this line: `pub type System = frame_system;`",
|
|
)
|
|
})?;
|
|
if !system_pallet.cfg_pattern.is_empty() {
|
|
return Err(syn::Error::new(
|
|
system_pallet.name.span(),
|
|
"`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes",
|
|
))
|
|
}
|
|
|
|
let features = pallets
|
|
.iter()
|
|
.filter_map(|decl| {
|
|
(!decl.cfg_pattern.is_empty()).then(|| {
|
|
decl.cfg_pattern.iter().flat_map(|attr| {
|
|
attr.predicates().filter_map(|pred| match pred {
|
|
Predicate::Feature(feat) => Some(feat),
|
|
Predicate::Test => Some("test"),
|
|
_ => None,
|
|
})
|
|
})
|
|
})
|
|
})
|
|
.flatten()
|
|
.collect::<HashSet<_>>();
|
|
|
|
let hidden_crate_name = "construct_runtime";
|
|
let scrate = generate_crate_access(hidden_crate_name, "frame-support");
|
|
let scrate_decl = generate_hidden_includes(hidden_crate_name, "frame-support");
|
|
|
|
let frame_system = generate_access_from_frame_or_crate("frame-system")?;
|
|
let block = quote!(<#name as #frame_system::Config>::Block);
|
|
let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic);
|
|
|
|
let mut dispatch = None;
|
|
let mut outer_event = None;
|
|
let mut outer_error = None;
|
|
let mut outer_origin = None;
|
|
let mut freeze_reason = None;
|
|
let mut hold_reason = None;
|
|
let mut slash_reason = None;
|
|
let mut lock_id = None;
|
|
let mut task = None;
|
|
|
|
for runtime_type in runtime_types.iter() {
|
|
match runtime_type {
|
|
RuntimeType::RuntimeCall(_) => {
|
|
dispatch =
|
|
Some(expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate));
|
|
},
|
|
RuntimeType::RuntimeEvent(_) => {
|
|
outer_event = Some(expand::expand_outer_enum(
|
|
&name,
|
|
&pallets,
|
|
&scrate,
|
|
expand::OuterEnumType::Event,
|
|
)?);
|
|
},
|
|
RuntimeType::RuntimeError(_) => {
|
|
outer_error = Some(expand::expand_outer_enum(
|
|
&name,
|
|
&pallets,
|
|
&scrate,
|
|
expand::OuterEnumType::Error,
|
|
)?);
|
|
},
|
|
RuntimeType::RuntimeOrigin(_) => {
|
|
outer_origin =
|
|
Some(expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?);
|
|
},
|
|
RuntimeType::RuntimeFreezeReason(_) => {
|
|
freeze_reason = Some(expand::expand_outer_freeze_reason(&pallets, &scrate));
|
|
},
|
|
RuntimeType::RuntimeHoldReason(_) => {
|
|
hold_reason = Some(expand::expand_outer_hold_reason(&pallets, &scrate));
|
|
},
|
|
RuntimeType::RuntimeSlashReason(_) => {
|
|
slash_reason = Some(expand::expand_outer_slash_reason(&pallets, &scrate));
|
|
},
|
|
RuntimeType::RuntimeLockId(_) => {
|
|
lock_id = Some(expand::expand_outer_lock_id(&pallets, &scrate));
|
|
},
|
|
RuntimeType::RuntimeTask(_) => {
|
|
task = Some(expand::expand_outer_task(&name, &pallets, &scrate));
|
|
},
|
|
}
|
|
}
|
|
|
|
let all_pallets = decl_all_pallets(&name, pallets.iter(), &features);
|
|
let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate);
|
|
|
|
let metadata = expand::expand_runtime_metadata(
|
|
&name,
|
|
&pallets,
|
|
&scrate,
|
|
&unchecked_extrinsic,
|
|
&system_pallet.path,
|
|
);
|
|
let outer_config = expand::expand_outer_config(&name, &pallets, &scrate);
|
|
let inherent =
|
|
expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate);
|
|
let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate);
|
|
let integrity_test = decl_integrity_test(&scrate);
|
|
let static_assertions = decl_static_assertions(&name, &pallets, &scrate);
|
|
|
|
let res = quote!(
|
|
#scrate_decl
|
|
|
|
// Prevent UncheckedExtrinsic to print unused warning.
|
|
const _: () = {
|
|
#[allow(unused)]
|
|
type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic;
|
|
};
|
|
|
|
#[derive(
|
|
Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug,
|
|
#scrate::__private::scale_info::TypeInfo
|
|
)]
|
|
pub struct #name;
|
|
impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name {
|
|
type RuntimeBlock = #block;
|
|
}
|
|
|
|
// Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata.
|
|
// The function is implemented by calling `impl_runtime_apis!`.
|
|
//
|
|
// However, the `runtime` may be used without calling `impl_runtime_apis!`.
|
|
// Rely on the `Deref` trait to differentiate between a runtime that implements
|
|
// APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro runtime).
|
|
//
|
|
// Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function.
|
|
// `InternalConstructRuntime` is implemented by the `runtime` for Runtime references (`& Runtime`),
|
|
// while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`).
|
|
//
|
|
// Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!`
|
|
// when both macros are called; and will resolve an empty `runtime_metadata` when only the `runtime`
|
|
// is used.
|
|
|
|
#[doc(hidden)]
|
|
trait InternalConstructRuntime {
|
|
#[inline(always)]
|
|
fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> {
|
|
Default::default()
|
|
}
|
|
}
|
|
#[doc(hidden)]
|
|
impl InternalConstructRuntime for &#name {}
|
|
|
|
#outer_event
|
|
|
|
#outer_error
|
|
|
|
#outer_origin
|
|
|
|
#all_pallets
|
|
|
|
#pallet_to_index
|
|
|
|
#dispatch
|
|
|
|
#task
|
|
|
|
#metadata
|
|
|
|
#outer_config
|
|
|
|
#inherent
|
|
|
|
#validate_unsigned
|
|
|
|
#freeze_reason
|
|
|
|
#hold_reason
|
|
|
|
#lock_id
|
|
|
|
#slash_reason
|
|
|
|
#integrity_test
|
|
|
|
#static_assertions
|
|
);
|
|
|
|
Ok(res)
|
|
}
|