Construct Runtime v2 (#1378)

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 <>
This commit is contained in:
gupnik
2024-03-13 12:31:01 +05:30
committed by GitHub
parent a756baf3b2
commit 82f3c3e2e8
45 changed files with 3211 additions and 204 deletions
@@ -28,6 +28,8 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream {
syn::Ident::new(&format!("__tt_default_parts_{}", count), def.item.span());
let extra_parts_unique_id =
syn::Ident::new(&format!("__tt_extra_parts_{}", count), def.item.span());
let default_parts_unique_id_v2 =
syn::Ident::new(&format!("__tt_default_parts_v2_{}", count), def.item.span());
let call_part = def.call.as_ref().map(|_| quote::quote!(Call,));
@@ -81,6 +83,58 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream {
.any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_)))
.then_some(quote::quote!(SlashReason,));
let call_part_v2 = def.call.as_ref().map(|_| quote::quote!(+ Call));
let task_part_v2 = def.task_enum.as_ref().map(|_| quote::quote!(+ Task));
let storage_part_v2 = (!def.storages.is_empty()).then(|| quote::quote!(+ Storage));
let event_part_v2 = def.event.as_ref().map(|event| {
let gen = event.gen_kind.is_generic().then(|| quote::quote!(<T>));
quote::quote!(+ Event #gen)
});
let error_part_v2 = def.error.as_ref().map(|_| quote::quote!(+ Error<T>));
let origin_part_v2 = def.origin.as_ref().map(|origin| {
let gen = origin.is_generic.then(|| quote::quote!(<T>));
quote::quote!(+ Origin #gen)
});
let config_part_v2 = def.genesis_config.as_ref().map(|genesis_config| {
let gen = genesis_config.gen_kind.is_generic().then(|| quote::quote!(<T>));
quote::quote!(+ Config #gen)
});
let inherent_part_v2 = def.inherent.as_ref().map(|_| quote::quote!(+ Inherent));
let validate_unsigned_part_v2 =
def.validate_unsigned.as_ref().map(|_| quote::quote!(+ ValidateUnsigned));
let freeze_reason_part_v2 = def
.composites
.iter()
.any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_)))
.then_some(quote::quote!(+ FreezeReason));
let hold_reason_part_v2 = def
.composites
.iter()
.any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_)))
.then_some(quote::quote!(+ HoldReason));
let lock_id_part_v2 = def
.composites
.iter()
.any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_)))
.then_some(quote::quote!(+ LockId));
let slash_reason_part_v2 = def
.composites
.iter()
.any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_)))
.then_some(quote::quote!(+ SlashReason));
quote::quote!(
// This macro follows the conventions as laid out by the `tt-call` crate. It does not
// accept any arguments and simply returns the pallet parts, separated by commas, then
@@ -138,5 +192,25 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream {
}
pub use #extra_parts_unique_id as tt_extra_parts;
#[macro_export]
#[doc(hidden)]
macro_rules! #default_parts_unique_id_v2 {
{
$caller:tt
frame_support = [{ $($frame_support:ident)::* }]
} => {
$($frame_support)*::__private::tt_return! {
$caller
tokens = [{
+ Pallet #call_part_v2 #storage_part_v2 #event_part_v2 #error_part_v2 #origin_part_v2 #config_part_v2
#inherent_part_v2 #validate_unsigned_part_v2 #freeze_reason_part_v2 #task_part_v2
#hold_reason_part_v2 #lock_id_part_v2 #slash_reason_part_v2
}]
}
};
}
pub use #default_parts_unique_id_v2 as tt_default_parts_v2;
)
}
@@ -148,6 +148,12 @@ impl MutItemAttrs for syn::ImplItemFn {
}
}
impl MutItemAttrs for syn::ItemType {
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
Some(&mut self.attrs)
}
}
/// Parse for `()`
struct Unit;
impl syn::parse::Parse for Unit {