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
@@ -0,0 +1,99 @@
// 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 crate::construct_runtime::parse::{Pallet, PalletPart, PalletPartKeyword, PalletPath};
use quote::ToTokens;
use syn::{punctuated::Punctuated, spanned::Spanned, token, Error, Ident, PathArguments};
impl Pallet {
pub fn try_from(
attr_span: proc_macro2::Span,
item: &syn::ItemType,
pallet_index: u8,
disable_call: bool,
disable_unsigned: bool,
bounds: &Punctuated<syn::TypeParamBound, token::Plus>,
) -> syn::Result<Self> {
let name = item.ident.clone();
let mut pallet_path = None;
let mut pallet_parts = vec![];
for (index, bound) in bounds.into_iter().enumerate() {
if let syn::TypeParamBound::Trait(syn::TraitBound { path, .. }) = bound {
if index == 0 {
pallet_path = Some(PalletPath { inner: path.clone() });
} else {
let pallet_part = syn::parse2::<PalletPart>(bound.into_token_stream())?;
pallet_parts.push(pallet_part);
}
} else {
return Err(Error::new(
attr_span,
"Invalid pallet declaration, expected a path or a trait object",
))
};
}
let mut path = pallet_path.ok_or(Error::new(
attr_span,
"Invalid pallet declaration, expected a path or a trait object",
))?;
let mut instance = None;
if let Some(segment) = path.inner.segments.iter_mut().find(|seg| !seg.arguments.is_empty())
{
if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
args, ..
}) = segment.arguments.clone()
{
if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() {
instance =
Some(Ident::new(&arg_path.to_token_stream().to_string(), arg_path.span()));
segment.arguments = PathArguments::None;
}
}
}
pallet_parts = pallet_parts
.into_iter()
.filter(|part| {
if let (true, &PalletPartKeyword::Call(_)) = (disable_call, &part.keyword) {
false
} else if let (true, &PalletPartKeyword::ValidateUnsigned(_)) =
(disable_unsigned, &part.keyword)
{
false
} else {
true
}
})
.collect();
let cfg_pattern = vec![];
Ok(Pallet {
is_expanded: true,
name,
index: pallet_index,
path,
instance,
cfg_pattern,
pallet_parts,
})
}
}