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
+27
View File
@@ -0,0 +1,27 @@
title: Construct Runtime V2 - An outer macro approach to define the runtime
doc:
- audience: Runtime Dev
description: |
Introduces `#[frame_support::runtime]` that can be attached to a mod to define a runtime. The items
in this mod can be attached to the following attributes to define the key components of the runtime.
1. `#[runtime::runtime]` attached to a struct defines the main runtime
2. `#[runtime::derive]` attached to the runtime struct defines the types generated by the runtime
3. `#[runtime::pallet_index]` must be attached to a pallet to define its index
4. `#[runtime::disable_call]` can be optionally attached to a pallet to disable its calls
5. `#[runtime::disable_unsigned]` can be optionally attached to a pallet to disable unsigned calls
6. 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.
migrations:
db: []
runtime: []
crates:
- name: frame-support
- name: frame-support-procedural
host_functions: []
+1 -1
View File
@@ -58,7 +58,7 @@ sp-io = { path = "../../../primitives/io", default-features = false }
frame-executive = { path = "../../../frame/executive", default-features = false }
frame-benchmarking = { path = "../../../frame/benchmarking", default-features = false }
frame-benchmarking-pallet-pov = { path = "../../../frame/benchmarking/pov", default-features = false }
frame-support = { path = "../../../frame/support", default-features = false, features = ["tuples-96"] }
frame-support = { path = "../../../frame/support", default-features = false, features = ["experimental", "tuples-96"] }
frame-system = { path = "../../../frame/system", default-features = false }
frame-system-benchmarking = { path = "../../../frame/system/benchmarking", default-features = false, optional = true }
frame-election-provider-support = { path = "../../../frame/election-provider-support", default-features = false }
+255 -87
View File
@@ -28,7 +28,7 @@ use frame_election_provider_support::{
onchain, BalancingConfig, ElectionDataProvider, SequentialPhragmen, VoteWeight,
};
use frame_support::{
construct_runtime, derive_impl,
derive_impl,
dispatch::DispatchClass,
dynamic_params::{dynamic_pallet_params, dynamic_params},
genesis_builder_helper::{build_config, create_default_config},
@@ -2196,92 +2196,260 @@ impl pallet_parameters::Config for Runtime {
type WeightInfo = ();
}
construct_runtime!(
pub enum Runtime {
System: frame_system,
Utility: pallet_utility,
Babe: pallet_babe,
Timestamp: pallet_timestamp,
// Authorship must be before session in order to note author in the correct session and era
// for im-online and staking.
Authorship: pallet_authorship,
Indices: pallet_indices,
Balances: pallet_balances,
TransactionPayment: pallet_transaction_payment,
AssetTxPayment: pallet_asset_tx_payment,
AssetConversionTxPayment: pallet_asset_conversion_tx_payment,
ElectionProviderMultiPhase: pallet_election_provider_multi_phase,
Staking: pallet_staking,
Session: pallet_session,
Democracy: pallet_democracy,
Council: pallet_collective::<Instance1>,
TechnicalCommittee: pallet_collective::<Instance2>,
Elections: pallet_elections_phragmen,
TechnicalMembership: pallet_membership::<Instance1>,
Grandpa: pallet_grandpa,
Treasury: pallet_treasury,
AssetRate: pallet_asset_rate,
Contracts: pallet_contracts,
Sudo: pallet_sudo,
ImOnline: pallet_im_online,
AuthorityDiscovery: pallet_authority_discovery,
Offences: pallet_offences,
Historical: pallet_session_historical,
RandomnessCollectiveFlip: pallet_insecure_randomness_collective_flip,
Identity: pallet_identity,
Society: pallet_society,
Recovery: pallet_recovery,
Vesting: pallet_vesting,
Scheduler: pallet_scheduler,
Glutton: pallet_glutton,
Preimage: pallet_preimage,
Proxy: pallet_proxy,
Multisig: pallet_multisig,
Bounties: pallet_bounties,
Tips: pallet_tips,
Assets: pallet_assets::<Instance1>,
PoolAssets: pallet_assets::<Instance2>,
Beefy: pallet_beefy,
// MMR leaf construction must be after session in order to have a leaf's next_auth_set
// refer to block<N>. See issue polkadot-fellows/runtimes#160 for details.
Mmr: pallet_mmr,
MmrLeaf: pallet_beefy_mmr,
Lottery: pallet_lottery,
Nis: pallet_nis,
Uniques: pallet_uniques,
Nfts: pallet_nfts,
NftFractionalization: pallet_nft_fractionalization,
Salary: pallet_salary,
CoreFellowship: pallet_core_fellowship,
TransactionStorage: pallet_transaction_storage,
VoterList: pallet_bags_list::<Instance1>,
StateTrieMigration: pallet_state_trie_migration,
ChildBounties: pallet_child_bounties,
Referenda: pallet_referenda,
Remark: pallet_remark,
RootTesting: pallet_root_testing,
ConvictionVoting: pallet_conviction_voting,
Whitelist: pallet_whitelist,
AllianceMotion: pallet_collective::<Instance3>,
Alliance: pallet_alliance,
NominationPools: pallet_nomination_pools,
RankedPolls: pallet_referenda::<Instance2>,
RankedCollective: pallet_ranked_collective,
AssetConversion: pallet_asset_conversion,
FastUnstake: pallet_fast_unstake,
MessageQueue: pallet_message_queue,
Pov: frame_benchmarking_pallet_pov,
TxPause: pallet_tx_pause,
SafeMode: pallet_safe_mode,
Statement: pallet_statement,
MultiBlockMigrations: pallet_migrations,
Broker: pallet_broker,
TasksExample: pallet_example_tasks,
Mixnet: pallet_mixnet,
Parameters: pallet_parameters,
SkipFeelessPayment: pallet_skip_feeless_payment,
}
);
#[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 Utility = pallet_utility;
#[runtime::pallet_index(2)]
pub type Babe = pallet_babe;
#[runtime::pallet_index(3)]
pub type Timestamp = pallet_timestamp;
// Authorship must be before session in order to note author in the correct session and era
// for im-online and staking.
#[runtime::pallet_index(4)]
pub type Authorship = pallet_authorship;
#[runtime::pallet_index(5)]
pub type Indices = pallet_indices;
#[runtime::pallet_index(6)]
pub type Balances = pallet_balances;
#[runtime::pallet_index(7)]
pub type TransactionPayment = pallet_transaction_payment;
#[runtime::pallet_index(8)]
pub type AssetTxPayment = pallet_asset_tx_payment;
#[runtime::pallet_index(9)]
pub type AssetConversionTxPayment = pallet_asset_conversion_tx_payment;
#[runtime::pallet_index(10)]
pub type ElectionProviderMultiPhase = pallet_election_provider_multi_phase;
#[runtime::pallet_index(11)]
pub type Staking = pallet_staking;
#[runtime::pallet_index(12)]
pub type Session = pallet_session;
#[runtime::pallet_index(13)]
pub type Democracy = pallet_democracy;
#[runtime::pallet_index(14)]
pub type Council = pallet_collective<Instance1>;
#[runtime::pallet_index(15)]
pub type TechnicalCommittee = pallet_collective<Instance2>;
#[runtime::pallet_index(16)]
pub type Elections = pallet_elections_phragmen;
#[runtime::pallet_index(17)]
pub type TechnicalMembership = pallet_membership<Instance1>;
#[runtime::pallet_index(18)]
pub type Grandpa = pallet_grandpa;
#[runtime::pallet_index(19)]
pub type Treasury = pallet_treasury;
#[runtime::pallet_index(20)]
pub type AssetRate = pallet_asset_rate;
#[runtime::pallet_index(21)]
pub type Contracts = pallet_contracts;
#[runtime::pallet_index(22)]
pub type Sudo = pallet_sudo;
#[runtime::pallet_index(23)]
pub type ImOnline = pallet_im_online;
#[runtime::pallet_index(24)]
pub type AuthorityDiscovery = pallet_authority_discovery;
#[runtime::pallet_index(25)]
pub type Offences = pallet_offences;
#[runtime::pallet_index(26)]
pub type Historical = pallet_session_historical;
#[runtime::pallet_index(27)]
pub type RandomnessCollectiveFlip = pallet_insecure_randomness_collective_flip;
#[runtime::pallet_index(28)]
pub type Identity = pallet_identity;
#[runtime::pallet_index(29)]
pub type Society = pallet_society;
#[runtime::pallet_index(30)]
pub type Recovery = pallet_recovery;
#[runtime::pallet_index(31)]
pub type Vesting = pallet_vesting;
#[runtime::pallet_index(32)]
pub type Scheduler = pallet_scheduler;
#[runtime::pallet_index(33)]
pub type Glutton = pallet_glutton;
#[runtime::pallet_index(34)]
pub type Preimage = pallet_preimage;
#[runtime::pallet_index(35)]
pub type Proxy = pallet_proxy;
#[runtime::pallet_index(36)]
pub type Multisig = pallet_multisig;
#[runtime::pallet_index(37)]
pub type Bounties = pallet_bounties;
#[runtime::pallet_index(38)]
pub type Tips = pallet_tips;
#[runtime::pallet_index(39)]
pub type Assets = pallet_assets<Instance1>;
#[runtime::pallet_index(40)]
pub type PoolAssets = pallet_assets<Instance2>;
#[runtime::pallet_index(41)]
pub type Beefy = pallet_beefy;
// MMR leaf construction must be after session in order to have a leaf's next_auth_set
// refer to block<N>. See issue polkadot-fellows/runtimes#160 for details.
#[runtime::pallet_index(42)]
pub type Mmr = pallet_mmr;
#[runtime::pallet_index(43)]
pub type MmrLeaf = pallet_beefy_mmr;
#[runtime::pallet_index(44)]
pub type Lottery = pallet_lottery;
#[runtime::pallet_index(45)]
pub type Nis = pallet_nis;
#[runtime::pallet_index(46)]
pub type Uniques = pallet_uniques;
#[runtime::pallet_index(47)]
pub type Nfts = pallet_nfts;
#[runtime::pallet_index(48)]
pub type NftFractionalization = pallet_nft_fractionalization;
#[runtime::pallet_index(49)]
pub type Salary = pallet_salary;
#[runtime::pallet_index(50)]
pub type CoreFellowship = pallet_core_fellowship;
#[runtime::pallet_index(51)]
pub type TransactionStorage = pallet_transaction_storage;
#[runtime::pallet_index(52)]
pub type VoterList = pallet_bags_list<Instance1>;
#[runtime::pallet_index(53)]
pub type StateTrieMigration = pallet_state_trie_migration;
#[runtime::pallet_index(54)]
pub type ChildBounties = pallet_child_bounties;
#[runtime::pallet_index(55)]
pub type Referenda = pallet_referenda;
#[runtime::pallet_index(56)]
pub type Remark = pallet_remark;
#[runtime::pallet_index(57)]
pub type RootTesting = pallet_root_testing;
#[runtime::pallet_index(58)]
pub type ConvictionVoting = pallet_conviction_voting;
#[runtime::pallet_index(59)]
pub type Whitelist = pallet_whitelist;
#[runtime::pallet_index(60)]
pub type AllianceMotion = pallet_collective<Instance3>;
#[runtime::pallet_index(61)]
pub type Alliance = pallet_alliance;
#[runtime::pallet_index(62)]
pub type NominationPools = pallet_nomination_pools;
#[runtime::pallet_index(63)]
pub type RankedPolls = pallet_referenda<Instance2>;
#[runtime::pallet_index(64)]
pub type RankedCollective = pallet_ranked_collective;
#[runtime::pallet_index(65)]
pub type AssetConversion = pallet_asset_conversion;
#[runtime::pallet_index(66)]
pub type FastUnstake = pallet_fast_unstake;
#[runtime::pallet_index(67)]
pub type MessageQueue = pallet_message_queue;
#[runtime::pallet_index(68)]
pub type Pov = frame_benchmarking_pallet_pov;
#[runtime::pallet_index(69)]
pub type TxPause = pallet_tx_pause;
#[runtime::pallet_index(70)]
pub type SafeMode = pallet_safe_mode;
#[runtime::pallet_index(71)]
pub type Statement = pallet_statement;
#[runtime::pallet_index(72)]
pub type MultiBlockMigrations = pallet_migrations;
#[runtime::pallet_index(73)]
pub type Broker = pallet_broker;
#[runtime::pallet_index(74)]
pub type TasksExample = pallet_example_tasks;
#[runtime::pallet_index(75)]
pub type Mixnet = pallet_mixnet;
#[runtime::pallet_index(76)]
pub type Parameters = pallet_parameters;
#[runtime::pallet_index(77)]
pub type SkipFeelessPayment = pallet_skip_feeless_payment;
}
/// The address format for describing accounts.
pub type Address = sp_runtime::MultiAddress<AccountId, AccountIndex>;
+3 -1
View File
@@ -109,7 +109,9 @@ try-runtime = [
"sp-debug-derive/force-debug",
"sp-runtime/try-runtime",
]
experimental = []
experimental = [
"frame-support-procedural/experimental",
]
# By default some types have documentation, `no-metadata-docs` allows to reduce the documentation
# in the metadata.
no-metadata-docs = [
@@ -38,6 +38,7 @@ regex = "1"
default = ["std"]
std = ["sp-crypto-hashing/std"]
no-metadata-docs = []
experimental = []
# Generate impl-trait for tuples with the given number of tuples. Will be needed as the number of
# pallets in a runtime grows. Does increase the compile time!
tuples-96 = []
@@ -208,8 +208,8 @@
//! This macro returns the ` :: expanded { Error }` list of additional parts we would like to
//! expose.
mod expand;
mod parse;
pub(crate) mod expand;
pub(crate) mod parse;
use crate::pallet::parse::helper::two128_str;
use cfg_expr::Predicate;
@@ -515,7 +515,7 @@ fn construct_runtime_final_expansion(
Ok(res)
}
fn decl_all_pallets<'a>(
pub(crate) fn decl_all_pallets<'a>(
runtime: &'a Ident,
pallet_declarations: impl Iterator<Item = &'a Pallet>,
features: &HashSet<&str>,
@@ -624,7 +624,8 @@ fn decl_all_pallets<'a>(
#( #all_pallets_without_system )*
)
}
fn decl_pallet_runtime_setup(
pub(crate) fn decl_pallet_runtime_setup(
runtime: &Ident,
pallet_declarations: &[Pallet],
scrate: &TokenStream2,
@@ -730,7 +731,7 @@ fn decl_pallet_runtime_setup(
)
}
fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 {
pub(crate) fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 {
quote!(
#[cfg(test)]
mod __construct_runtime_integrity_test {
@@ -745,7 +746,7 @@ fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 {
)
}
fn decl_static_assertions(
pub(crate) fn decl_static_assertions(
runtime: &Ident,
pallet_decls: &[Pallet],
scrate: &TokenStream2,
@@ -776,7 +777,7 @@ fn decl_static_assertions(
}
}
fn check_pallet_number(input: TokenStream2, pallet_num: usize) -> Result<()> {
pub(crate) fn check_pallet_number(input: TokenStream2, pallet_num: usize) -> Result<()> {
let max_pallet_num = {
if cfg!(feature = "tuples-96") {
96
@@ -31,6 +31,7 @@ mod match_and_insert;
mod no_bound;
mod pallet;
mod pallet_error;
mod runtime;
mod storage_alias;
mod transactional;
mod tt_macro;
@@ -1220,6 +1221,73 @@ pub fn import_section(attr: TokenStream, tokens: TokenStream) -> TokenStream {
.into()
}
/// Construct a runtime, with the given name and the given pallets.
///
/// # Example:
///
/// ```ignore
/// #[frame_support::runtime]
/// mod runtime {
/// // The main runtime
/// #[runtime::runtime]
/// // Runtime Types to be generated
/// #[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 Test = path::to::test;
///
/// // Pallet with instance.
/// #[runtime::pallet_index(2)]
/// pub type Test2_Instance1 = test2<Instance1>;
///
/// // Pallet with calls disabled.
/// #[runtime::pallet_index(3)]
/// #[runtime::disable_call]
/// pub type Test3 = test3;
///
/// // Pallet with unsigned extrinsics disabled.
/// #[runtime::pallet_index(4)]
/// #[runtime::disable_unsigned]
/// pub type Test4 = test4;
/// }
/// ```
///
/// # Legacy Ordering
///
/// 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.
///
/// # Note
///
/// The population of the genesis storage depends on the order of pallets. So, if one of your
/// pallets depends on another pallet, the pallet that is depended upon needs to come before
/// the pallet depending on it.
///
/// # Type definitions
///
/// * The macro generates a type alias for each pallet to their `Pallet`. E.g. `type System =
/// frame_system::Pallet<Runtime>`
#[cfg(feature = "experimental")]
#[proc_macro_attribute]
pub fn runtime(attr: TokenStream, item: TokenStream) -> TokenStream {
runtime::runtime(attr, item)
}
/// Mark a module that contains dynamic parameters.
///
/// See the `pallet_parameters` for a full example.
@@ -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 {
@@ -0,0 +1,320 @@
// 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)
}
@@ -0,0 +1,236 @@
// 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(),
}
}
@@ -0,0 +1,37 @@
// 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::pallet::parse::helper::MutItemAttrs;
use quote::ToTokens;
pub(crate) fn take_first_item_runtime_attr<Attr>(
item: &mut impl MutItemAttrs,
) -> syn::Result<Option<Attr>>
where
Attr: syn::parse::Parse,
{
let attrs = if let Some(attrs) = item.mut_item_attrs() { attrs } else { return Ok(None) };
if let Some(index) = attrs.iter().position(|attr| {
attr.path().segments.first().map_or(false, |segment| segment.ident == "runtime")
}) {
let runtime_attr = attrs.remove(index);
Ok(Some(syn::parse2(runtime_attr.into_token_stream())?))
} else {
Ok(None)
}
}
@@ -0,0 +1,266 @@
// 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.
pub mod helper;
pub mod pallet;
pub mod pallet_decl;
pub mod runtime_struct;
pub mod runtime_types;
use crate::construct_runtime::parse::Pallet;
use pallet_decl::PalletDeclaration;
use proc_macro2::TokenStream as TokenStream2;
use quote::ToTokens;
use std::collections::HashMap;
use syn::{spanned::Spanned, Ident, Token};
use frame_support_procedural_tools::syn_ext as ext;
use runtime_types::RuntimeType;
mod keyword {
use syn::custom_keyword;
custom_keyword!(runtime);
custom_keyword!(derive);
custom_keyword!(pallet_index);
custom_keyword!(disable_call);
custom_keyword!(disable_unsigned);
}
enum RuntimeAttr {
Runtime(proc_macro2::Span),
Derive(proc_macro2::Span, Vec<RuntimeType>),
PalletIndex(proc_macro2::Span, u8),
DisableCall(proc_macro2::Span),
DisableUnsigned(proc_macro2::Span),
}
impl RuntimeAttr {
fn span(&self) -> proc_macro2::Span {
match self {
Self::Runtime(span) => *span,
Self::Derive(span, _) => *span,
Self::PalletIndex(span, _) => *span,
Self::DisableCall(span) => *span,
Self::DisableUnsigned(span) => *span,
}
}
}
impl syn::parse::Parse for RuntimeAttr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<keyword::runtime>()?;
content.parse::<syn::Token![::]>()?;
let lookahead = content.lookahead1();
if lookahead.peek(keyword::runtime) {
Ok(RuntimeAttr::Runtime(content.parse::<keyword::runtime>()?.span()))
} else if lookahead.peek(keyword::derive) {
let _ = content.parse::<keyword::derive>();
let derive_content;
syn::parenthesized!(derive_content in content);
let runtime_types =
derive_content.parse::<ext::Punctuated<RuntimeType, Token![,]>>()?;
let runtime_types = runtime_types.inner.into_iter().collect();
Ok(RuntimeAttr::Derive(derive_content.span(), runtime_types))
} else if lookahead.peek(keyword::pallet_index) {
let _ = content.parse::<keyword::pallet_index>();
let pallet_index_content;
syn::parenthesized!(pallet_index_content in content);
let pallet_index = pallet_index_content.parse::<syn::LitInt>()?;
if !pallet_index.suffix().is_empty() {
let msg = "Number literal must not have a suffix";
return Err(syn::Error::new(pallet_index.span(), msg))
}
Ok(RuntimeAttr::PalletIndex(pallet_index.span(), pallet_index.base10_parse()?))
} else if lookahead.peek(keyword::disable_call) {
Ok(RuntimeAttr::DisableCall(content.parse::<keyword::disable_call>()?.span()))
} else if lookahead.peek(keyword::disable_unsigned) {
Ok(RuntimeAttr::DisableUnsigned(content.parse::<keyword::disable_unsigned>()?.span()))
} else {
Err(lookahead.error())
}
}
}
#[derive(Debug, Clone)]
pub enum AllPalletsDeclaration {
Implicit(ImplicitAllPalletsDeclaration),
Explicit(ExplicitAllPalletsDeclaration),
}
/// Declaration of a runtime with some pallet with implicit declaration of parts.
#[derive(Debug, Clone)]
pub struct ImplicitAllPalletsDeclaration {
pub name: Ident,
pub pallet_decls: Vec<PalletDeclaration>,
pub pallet_count: usize,
}
/// Declaration of a runtime with all pallet having explicit declaration of parts.
#[derive(Debug, Clone)]
pub struct ExplicitAllPalletsDeclaration {
pub name: Ident,
pub pallets: Vec<Pallet>,
}
pub struct Def {
pub input: TokenStream2,
pub item: syn::ItemMod,
pub runtime_struct: runtime_struct::RuntimeStructDef,
pub pallets: AllPalletsDeclaration,
pub runtime_types: Vec<RuntimeType>,
}
impl Def {
pub fn try_from(mut item: syn::ItemMod) -> syn::Result<Self> {
let input: TokenStream2 = item.to_token_stream().into();
let item_span = item.span();
let items = &mut item
.content
.as_mut()
.ok_or_else(|| {
let msg = "Invalid runtime definition, expected mod to be inlined.";
syn::Error::new(item_span, msg)
})?
.1;
let mut runtime_struct = None;
let mut runtime_types = None;
let mut indices = HashMap::new();
let mut names = HashMap::new();
let mut pallet_decls = vec![];
let mut pallets = vec![];
for item in items.iter_mut() {
let mut pallet_item = None;
let mut pallet_index = 0;
let mut disable_call = false;
let mut disable_unsigned = false;
while let Some(runtime_attr) =
helper::take_first_item_runtime_attr::<RuntimeAttr>(item)?
{
match runtime_attr {
RuntimeAttr::Runtime(span) if runtime_struct.is_none() => {
let p = runtime_struct::RuntimeStructDef::try_from(span, item)?;
runtime_struct = Some(p);
},
RuntimeAttr::Derive(_, types) if runtime_types.is_none() => {
runtime_types = Some(types);
},
RuntimeAttr::PalletIndex(span, index) => {
pallet_index = index;
pallet_item = if let syn::Item::Type(item) = item {
Some(item.clone())
} else {
let msg = "Invalid runtime::pallet_index, expected type definition";
return Err(syn::Error::new(span, msg))
};
},
RuntimeAttr::DisableCall(_) => disable_call = true,
RuntimeAttr::DisableUnsigned(_) => disable_unsigned = true,
attr => {
let msg = "Invalid duplicated attribute";
return Err(syn::Error::new(attr.span(), msg))
},
}
}
if let Some(pallet_item) = pallet_item {
match *pallet_item.ty.clone() {
syn::Type::Path(ref path) => {
let pallet_decl =
PalletDeclaration::try_from(item.span(), &pallet_item, path)?;
if let Some(used_pallet) =
names.insert(pallet_decl.name.clone(), pallet_decl.name.span())
{
let msg = "Two pallets with the same name!";
let mut err = syn::Error::new(used_pallet, &msg);
err.combine(syn::Error::new(pallet_decl.name.span(), &msg));
return Err(err)
}
pallet_decls.push(pallet_decl);
},
syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) => {
let pallet = Pallet::try_from(
item.span(),
&pallet_item,
pallet_index,
disable_call,
disable_unsigned,
&bounds,
)?;
if let Some(used_pallet) = indices.insert(pallet.index, pallet.name.clone())
{
let msg = format!(
"Pallet indices are conflicting: Both pallets {} and {} are at index {}",
used_pallet, pallet.name, pallet.index,
);
let mut err = syn::Error::new(used_pallet.span(), &msg);
err.combine(syn::Error::new(pallet.name.span(), msg));
return Err(err)
}
pallets.push(pallet);
},
_ => continue,
}
}
}
let name = item.ident.clone();
let decl_count = pallet_decls.len();
let pallets = if decl_count > 0 {
AllPalletsDeclaration::Implicit(ImplicitAllPalletsDeclaration {
name,
pallet_decls,
pallet_count: decl_count.saturating_add(pallets.len()),
})
} else {
AllPalletsDeclaration::Explicit(ExplicitAllPalletsDeclaration { name, pallets })
};
let def = Def {
input,
item,
runtime_struct: runtime_struct.ok_or_else(|| {
syn::Error::new(item_span,
"Missing Runtime. Please add a struct inside the module and annotate it with `#[runtime::runtime]`"
)
})?,
pallets,
runtime_types: runtime_types.ok_or_else(|| {
syn::Error::new(item_span,
"Missing Runtime Types. Please annotate the runtime struct with `#[runtime::derive]`"
)
})?,
};
Ok(def)
}
}
@@ -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,
})
}
}
@@ -0,0 +1,60 @@
// 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 quote::ToTokens;
use syn::{spanned::Spanned, Attribute, Ident, PathArguments};
/// The declaration of a pallet.
#[derive(Debug, Clone)]
pub struct PalletDeclaration {
/// The name of the pallet, e.g.`System` in `System: frame_system`.
pub name: Ident,
/// Optional attributes tagged right above a pallet declaration.
pub attrs: Vec<Attribute>,
/// The path of the pallet, e.g. `frame_system` in `System: frame_system`.
pub path: syn::Path,
/// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::<Instance1>`.
pub instance: Option<Ident>,
}
impl PalletDeclaration {
pub fn try_from(
_attr_span: proc_macro2::Span,
item: &syn::ItemType,
path: &syn::TypePath,
) -> syn::Result<Self> {
let name = item.ident.clone();
let mut path = path.path.clone();
let mut instance = None;
if let Some(segment) = path.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;
}
}
}
Ok(Self { name, path, instance, attrs: item.attrs.clone() })
}
}
@@ -0,0 +1,35 @@
// 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 syn::spanned::Spanned;
pub struct RuntimeStructDef {
pub ident: syn::Ident,
pub attr_span: proc_macro2::Span,
}
impl RuntimeStructDef {
pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Struct(item) = item {
item
} else {
let msg = "Invalid runtime::runtime, expected struct definition";
return Err(syn::Error::new(item.span(), msg))
};
Ok(Self { ident: item.ident.clone(), attr_span })
}
}
@@ -0,0 +1,76 @@
// 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 syn::{
parse::{Parse, ParseStream},
Result,
};
mod keyword {
use syn::custom_keyword;
custom_keyword!(RuntimeCall);
custom_keyword!(RuntimeEvent);
custom_keyword!(RuntimeError);
custom_keyword!(RuntimeOrigin);
custom_keyword!(RuntimeFreezeReason);
custom_keyword!(RuntimeHoldReason);
custom_keyword!(RuntimeSlashReason);
custom_keyword!(RuntimeLockId);
custom_keyword!(RuntimeTask);
}
#[derive(Debug, Clone, PartialEq)]
pub enum RuntimeType {
RuntimeCall(keyword::RuntimeCall),
RuntimeEvent(keyword::RuntimeEvent),
RuntimeError(keyword::RuntimeError),
RuntimeOrigin(keyword::RuntimeOrigin),
RuntimeFreezeReason(keyword::RuntimeFreezeReason),
RuntimeHoldReason(keyword::RuntimeHoldReason),
RuntimeSlashReason(keyword::RuntimeSlashReason),
RuntimeLockId(keyword::RuntimeLockId),
RuntimeTask(keyword::RuntimeTask),
}
impl Parse for RuntimeType {
fn parse(input: ParseStream) -> Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(keyword::RuntimeCall) {
Ok(Self::RuntimeCall(input.parse()?))
} else if lookahead.peek(keyword::RuntimeEvent) {
Ok(Self::RuntimeEvent(input.parse()?))
} else if lookahead.peek(keyword::RuntimeError) {
Ok(Self::RuntimeError(input.parse()?))
} else if lookahead.peek(keyword::RuntimeOrigin) {
Ok(Self::RuntimeOrigin(input.parse()?))
} else if lookahead.peek(keyword::RuntimeFreezeReason) {
Ok(Self::RuntimeFreezeReason(input.parse()?))
} else if lookahead.peek(keyword::RuntimeHoldReason) {
Ok(Self::RuntimeHoldReason(input.parse()?))
} else if lookahead.peek(keyword::RuntimeSlashReason) {
Ok(Self::RuntimeSlashReason(input.parse()?))
} else if lookahead.peek(keyword::RuntimeLockId) {
Ok(Self::RuntimeLockId(input.parse()?))
} else if lookahead.peek(keyword::RuntimeTask) {
Ok(Self::RuntimeTask(input.parse()?))
} else {
Err(lookahead.error())
}
}
}
+3
View File
@@ -510,6 +510,9 @@ pub use frame_support_procedural::{
construct_runtime, match_and_insert, transactional, PalletError, RuntimeDebugNoBound,
};
#[cfg(feature = "experimental")]
pub use frame_support_procedural::runtime;
#[doc(hidden)]
pub use frame_support_procedural::{__create_tt_macro, __generate_dummy_part_checker};
+1 -1
View File
@@ -24,7 +24,7 @@ sp-api = { path = "../../../primitives/api", default-features = false }
sp-arithmetic = { path = "../../../primitives/arithmetic", default-features = false }
sp-io = { path = "../../../primitives/io", default-features = false }
sp-state-machine = { path = "../../../primitives/state-machine", optional = true }
frame-support = { path = "..", default-features = false }
frame-support = { path = "..", default-features = false, features = ["experimental"] }
frame-benchmarking = { path = "../../benchmarking", default-features = false }
sp-runtime = { path = "../../../primitives/runtime", default-features = false }
sp-core = { path = "../../../primitives/core", default-features = false }
@@ -30,7 +30,7 @@ use scale_info::TypeInfo;
use sp_core::{sr25519, ConstU64};
use sp_runtime::{
generic,
traits::{BlakeTwo256, Verify},
traits::{BlakeTwo256, ValidateUnsigned, Verify},
DispatchError, ModuleError,
};
use sp_version::RuntimeVersion;
@@ -176,6 +176,17 @@ mod nested {
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {}
}
#[pallet::validate_unsigned]
impl<T: Config> ValidateUnsigned for Pallet<T> {
type Call = Call<T>;
fn validate_unsigned(
_source: TransactionSource,
_call: &Self::Call,
) -> TransactionValidity {
Err(TransactionValidityError::Invalid(InvalidTransaction::Call))
}
}
}
}
@@ -247,6 +258,20 @@ pub mod module3 {
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {}
}
#[pallet::storage]
pub type Storage<T> = StorageValue<_, u32>;
#[pallet::validate_unsigned]
impl<T: Config> ValidateUnsigned for Pallet<T> {
type Call = Call<T>;
fn validate_unsigned(
_source: TransactionSource,
_call: &Self::Call,
) -> TransactionValidity {
Err(TransactionValidityError::Invalid(InvalidTransaction::Call))
}
}
}
pub type BlockNumber = u64;
@@ -256,24 +281,64 @@ pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, RuntimeCall, Signature, ()>;
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
frame_support::construct_runtime!(
pub struct Runtime
{
System: frame_system::{Pallet, Call, Event<T>, Origin<T>} = 30,
Module1_1: module1::<Instance1>::{Pallet, Call, Storage, Event<T>, Origin<T>},
Module2: module2::{Pallet, Call, Storage, Event<T>, Origin},
Module1_2: module1::<Instance2>::{Pallet, Call, Storage, Event<T>, Origin<T>},
NestedModule3: nested::module3::{Pallet, Call, Config<T>, Storage, Event<T>, Origin},
Module3: self::module3::{Pallet, Call, Config<T>, Storage, Event<T>, Origin<T>},
Module1_3: module1::<Instance3>::{Pallet, Storage, Event<T> } = 6,
Module1_4: module1::<Instance4>::{Pallet, Call, Event<T> } = 3,
Module1_5: module1::<Instance5>::{Pallet, Event<T>},
Module1_6: module1::<Instance6>::{Pallet, Call, Storage, Event<T>, Origin<T>} = 1,
Module1_7: module1::<Instance7>::{Pallet, Call, Storage, Event<T>, Origin<T>},
Module1_8: module1::<Instance8>::{Pallet, Call, Storage, Event<T>, Origin<T>} = 12,
Module1_9: module1::<Instance9>::{Pallet, Call, Storage, Event<T>, Origin<T>},
}
);
#[frame_support::runtime]
mod runtime {
#[runtime::runtime]
#[runtime::derive(
RuntimeCall,
RuntimeEvent,
RuntimeError,
RuntimeOrigin,
RuntimeFreezeReason,
RuntimeHoldReason,
RuntimeSlashReason,
RuntimeLockId,
RuntimeTask
)]
pub struct Runtime;
#[runtime::pallet_index(30)]
pub type System = frame_system + Pallet + Call + Event<T> + Origin<T>;
#[runtime::pallet_index(31)]
pub type Module1_1 = module1<Instance1>;
#[runtime::pallet_index(32)]
pub type Module2 = module2;
#[runtime::pallet_index(33)]
pub type Module1_2 = module1<Instance2>;
#[runtime::pallet_index(34)]
pub type NestedModule3 = nested::module3;
#[runtime::pallet_index(35)]
#[runtime::disable_unsigned]
pub type Module3 = self::module3;
#[runtime::pallet_index(6)]
#[runtime::disable_call]
pub type Module1_3 = module1<Instance3>;
#[runtime::pallet_index(3)]
pub type Module1_4 = module1<Instance4>;
#[runtime::pallet_index(4)]
#[runtime::disable_call]
pub type Module1_5 = module1<Instance5>;
#[runtime::pallet_index(1)]
pub type Module1_6 = module1<Instance6>;
#[runtime::pallet_index(2)]
pub type Module1_7 = module1<Instance7>;
#[runtime::pallet_index(12)]
pub type Module1_8 = module1<Instance8>;
#[runtime::pallet_index(13)]
pub type Module1_9 = module1<Instance9>;
}
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
impl frame_system::Config for Runtime {
@@ -605,17 +670,17 @@ fn get_module_names() {
let module_names = RuntimeCall::get_module_names();
assert_eq!(
[
"Module1_6",
"Module1_7",
"Module1_4",
"Module1_8",
"Module1_9",
"System",
"Module1_1",
"Module2",
"Module1_2",
"NestedModule3",
"Module3",
"Module1_4",
"Module1_6",
"Module1_7",
"Module1_8",
"Module1_9",
],
module_names
);
@@ -636,9 +701,13 @@ fn call_subtype_conversion() {
#[test]
fn test_metadata() {
use frame_metadata::{v14::*, *};
use frame_metadata::{
v14::{StorageEntryType::Plain, *},
*,
};
use scale_info::meta_type;
use sp_core::Encode;
use sp_metadata_ir::StorageEntryModifierIR::Optional;
fn maybe_docs(doc: Vec<&'static str>) -> Vec<&'static str> {
if cfg!(feature = "no-metadata-docs") {
@@ -649,6 +718,69 @@ fn test_metadata() {
}
let pallets = vec![
PalletMetadata {
name: "Module1_6",
storage:None,
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance6>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance6>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance6>>().into()),
index: 1,
},
PalletMetadata {
name: "Module1_7",
storage: None,
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance7>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance7>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance7>>().into()),
index: 2,
},
PalletMetadata {
name: "Module1_4",
storage: None,
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance4>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance4>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance4>>().into()),
index: 3,
},
PalletMetadata {
name: "Module1_5",
storage: None,
calls: None,
event: Some(meta_type::<module1::Event<Runtime, module1::Instance5>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance5>>().into()),
index: 4,
},
PalletMetadata {
name: "Module1_3",
storage: None,
calls: None,
event: Some(meta_type::<module1::Event<Runtime, module1::Instance3>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance3>>().into()),
index: 6,
},
PalletMetadata {
name: "Module1_8",
storage: None,
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance8>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance8>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance8>>().into()),
index: 12,
},
PalletMetadata {
name: "Module1_9",
storage: None,
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance9>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance9>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance9>>().into()),
index: 13,
},
PalletMetadata {
name: "System",
storage: None,
@@ -703,7 +835,7 @@ fn test_metadata() {
},
PalletMetadata {
name: "Module1_1",
storage: Some(PalletStorageMetadata { prefix: "Module1_1", entries: vec![] }),
storage: None,
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance1>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance1>>().into()),
constants: vec![],
@@ -712,7 +844,7 @@ fn test_metadata() {
},
PalletMetadata {
name: "Module2",
storage: Some(PalletStorageMetadata { prefix: "Module2", entries: vec![] }),
storage: None,
calls: Some(meta_type::<module2::Call<Runtime>>().into()),
event: Some(meta_type::<module2::Event<Runtime>>().into()),
constants: vec![],
@@ -721,7 +853,7 @@ fn test_metadata() {
},
PalletMetadata {
name: "Module1_2",
storage: Some(PalletStorageMetadata { prefix: "Module1_2", entries: vec![] }),
storage: None,
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance2>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance2>>().into()),
constants: vec![],
@@ -730,7 +862,7 @@ fn test_metadata() {
},
PalletMetadata {
name: "NestedModule3",
storage: Some(PalletStorageMetadata { prefix: "NestedModule3", entries: vec![] }),
storage: None,
calls: Some(meta_type::<nested::module3::Call<Runtime>>().into()),
event: Some(meta_type::<nested::module3::Event<Runtime>>().into()),
constants: vec![],
@@ -739,76 +871,24 @@ fn test_metadata() {
},
PalletMetadata {
name: "Module3",
storage: Some(PalletStorageMetadata { prefix: "Module3", entries: vec![] }),
storage: Some(PalletStorageMetadata {
prefix: "Module3",
entries: vec![
StorageEntryMetadata {
name: "Storage",
modifier: Optional.into(),
ty: Plain(meta_type::<u32>().into()),
default: vec![0],
docs: vec![],
},
]
}),
calls: Some(meta_type::<module3::Call<Runtime>>().into()),
event: Some(meta_type::<module3::Event<Runtime>>().into()),
constants: vec![],
error: Some(meta_type::<module3::Error<Runtime>>().into()),
index: 35,
},
PalletMetadata {
name: "Module1_3",
storage: Some(PalletStorageMetadata { prefix: "Module1_3", entries: vec![] }),
calls: None,
event: Some(meta_type::<module1::Event<Runtime, module1::Instance3>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance3>>().into()),
index: 6,
},
PalletMetadata {
name: "Module1_4",
storage: None,
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance4>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance4>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance4>>().into()),
index: 3,
},
PalletMetadata {
name: "Module1_5",
storage: None,
calls: None,
event: Some(meta_type::<module1::Event<Runtime, module1::Instance5>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance5>>().into()),
index: 4,
},
PalletMetadata {
name: "Module1_6",
storage: Some(PalletStorageMetadata { prefix: "Module1_6", entries: vec![] }),
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance6>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance6>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance6>>().into()),
index: 1,
},
PalletMetadata {
name: "Module1_7",
storage: Some(PalletStorageMetadata { prefix: "Module1_7", entries: vec![] }),
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance7>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance7>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance7>>().into()),
index: 2,
},
PalletMetadata {
name: "Module1_8",
storage: Some(PalletStorageMetadata { prefix: "Module1_8", entries: vec![] }),
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance8>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance8>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance8>>().into()),
index: 12,
},
PalletMetadata {
name: "Module1_9",
storage: Some(PalletStorageMetadata { prefix: "Module1_9", entries: vec![] }),
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance9>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance9>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance9>>().into()),
index: 13,
},
];
let extrinsic = ExtrinsicMetadata {
@@ -895,3 +975,19 @@ fn pallet_in_runtime_is_correct() {
assert_eq!(PalletInfo::module_name::<Module1_9>().unwrap(), "module1");
assert!(PalletInfo::crate_version::<Module1_9>().is_some());
}
#[test]
fn test_validate_unsigned() {
use frame_support::pallet_prelude::*;
let call = RuntimeCall::NestedModule3(nested::module3::Call::fail {});
let validity = Runtime::validate_unsigned(TransactionSource::Local, &call).unwrap_err();
assert_eq!(validity, TransactionValidityError::Invalid(InvalidTransaction::Call));
let call = RuntimeCall::Module3(module3::Call::fail {});
let validity = Runtime::validate_unsigned(TransactionSource::Local, &call).unwrap_err();
assert_eq!(
validity,
TransactionValidityError::Unknown(UnknownTransaction::NoUnsignedValidator)
);
}
@@ -0,0 +1,993 @@
// 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.
//! General tests for construct_runtime macro, test for:
//! * error declared with decl_error works
//! * integrity test is generated
#![recursion_limit = "128"]
use codec::MaxEncodedLen;
use frame_support::{
derive_impl, parameter_types, traits::PalletInfo as _, weights::RuntimeDbWeight,
};
use frame_system::limits::{BlockLength, BlockWeights};
use scale_info::TypeInfo;
use sp_core::{sr25519, ConstU64};
use sp_runtime::{
generic,
traits::{BlakeTwo256, ValidateUnsigned, Verify},
DispatchError, ModuleError,
};
use sp_version::RuntimeVersion;
parameter_types! {
pub static IntegrityTestExec: u32 = 0;
}
#[frame_support::pallet(dev_mode)]
mod module1 {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::pallet]
pub struct Pallet<T, I = ()>(_);
#[pallet::config]
pub trait Config<I: 'static = ()>: frame_system::Config {
type RuntimeEvent: From<Event<Self, I>>
+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
}
#[pallet::call]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub fn fail(_origin: OriginFor<T>) -> DispatchResult {
Err(Error::<T, I>::Something.into())
}
}
#[pallet::origin]
#[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
#[scale_info(skip_type_params(I))]
pub struct Origin<T, I = ()>(pub PhantomData<(T, I)>);
#[pallet::event]
pub enum Event<T: Config<I>, I: 'static = ()> {
A(<T as frame_system::Config>::AccountId),
}
#[pallet::error]
pub enum Error<T, I = ()> {
Something,
}
}
#[frame_support::pallet(dev_mode)]
mod module2 {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn integrity_test() {
IntegrityTestExec::mutate(|i| *i += 1);
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
pub fn fail(_origin: OriginFor<T>) -> DispatchResult {
Err(Error::<T>::Something.into())
}
}
#[pallet::origin]
#[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
pub struct Origin;
#[pallet::event]
pub enum Event<T> {
A,
}
#[pallet::error]
pub enum Error<T> {
Something,
}
}
mod nested {
use super::*;
#[frame_support::pallet(dev_mode)]
pub mod module3 {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>>
+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn integrity_test() {
IntegrityTestExec::mutate(|i| *i += 1);
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
pub fn fail(_origin: OriginFor<T>) -> DispatchResult {
Err(Error::<T>::Something.into())
}
}
#[pallet::origin]
#[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
pub struct Origin;
#[pallet::event]
pub enum Event<T> {
A,
}
#[pallet::error]
pub enum Error<T> {
Something,
}
#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
#[serde(skip)]
pub _config: sp_std::marker::PhantomData<T>,
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {}
}
#[pallet::validate_unsigned]
impl<T: Config> ValidateUnsigned for Pallet<T> {
type Call = Call<T>;
fn validate_unsigned(
_source: TransactionSource,
_call: &Self::Call,
) -> TransactionValidity {
Err(TransactionValidityError::Invalid(InvalidTransaction::Call))
}
}
}
}
#[frame_support::pallet(dev_mode)]
pub mod module3 {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
}
#[pallet::call]
impl<T: Config> Pallet<T> {
pub fn fail(_origin: OriginFor<T>) -> DispatchResult {
Err(Error::<T>::Something.into())
}
pub fn aux_1(_origin: OriginFor<T>, #[pallet::compact] _data: u32) -> DispatchResult {
unreachable!()
}
pub fn aux_2(
_origin: OriginFor<T>,
_data: i32,
#[pallet::compact] _data2: u32,
) -> DispatchResult {
unreachable!()
}
#[pallet::weight(0)]
pub fn aux_3(_origin: OriginFor<T>, _data: i32, _data2: String) -> DispatchResult {
unreachable!()
}
#[pallet::weight(3)]
pub fn aux_4(_origin: OriginFor<T>) -> DispatchResult {
unreachable!()
}
#[pallet::weight((5, DispatchClass::Operational))]
pub fn operational(_origin: OriginFor<T>) -> DispatchResult {
unreachable!()
}
}
#[pallet::origin]
#[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
pub struct Origin<T>(pub PhantomData<T>);
#[pallet::event]
pub enum Event<T> {
A,
}
#[pallet::error]
pub enum Error<T> {
Something,
}
#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
#[serde(skip)]
pub _config: sp_std::marker::PhantomData<T>,
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {}
}
#[pallet::storage]
pub type Storage<T> = StorageValue<_, u32>;
#[pallet::validate_unsigned]
impl<T: Config> ValidateUnsigned for Pallet<T> {
type Call = Call<T>;
fn validate_unsigned(
_source: TransactionSource,
_call: &Self::Call,
) -> TransactionValidity {
Err(TransactionValidityError::Invalid(InvalidTransaction::Call))
}
}
}
pub type BlockNumber = u64;
pub type Signature = sr25519::Signature;
pub type AccountId = <Signature as Verify>::Signer;
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, RuntimeCall, Signature, ()>;
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
#[frame_support::runtime(legacy_ordering)]
mod runtime {
#[runtime::runtime]
#[runtime::derive(
RuntimeCall,
RuntimeEvent,
RuntimeError,
RuntimeOrigin,
RuntimeFreezeReason,
RuntimeHoldReason,
RuntimeSlashReason,
RuntimeLockId,
RuntimeTask
)]
pub struct Runtime;
#[runtime::pallet_index(30)]
pub type System = frame_system + Pallet + Call + Event<T> + Origin<T>;
#[runtime::pallet_index(31)]
pub type Module1_1 = module1<Instance1>;
#[runtime::pallet_index(32)]
pub type Module2 = module2;
#[runtime::pallet_index(33)]
pub type Module1_2 = module1<Instance2>;
#[runtime::pallet_index(34)]
pub type NestedModule3 = nested::module3;
#[runtime::pallet_index(35)]
#[runtime::disable_unsigned]
pub type Module3 = self::module3;
#[runtime::pallet_index(6)]
#[runtime::disable_call]
pub type Module1_3 = module1<Instance3>;
#[runtime::pallet_index(3)]
pub type Module1_4 = module1<Instance4>;
#[runtime::pallet_index(4)]
#[runtime::disable_call]
pub type Module1_5 = module1<Instance5>;
#[runtime::pallet_index(1)]
pub type Module1_6 = module1<Instance6>;
#[runtime::pallet_index(2)]
pub type Module1_7 = module1<Instance7>;
#[runtime::pallet_index(12)]
pub type Module1_8 = module1<Instance8>;
#[runtime::pallet_index(13)]
pub type Module1_9 = module1<Instance9>;
}
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
impl frame_system::Config for Runtime {
type AccountId = AccountId;
type Lookup = sp_runtime::traits::IdentityLookup<AccountId>;
type BaseCallFilter = frame_support::traits::Everything;
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type RuntimeEvent = RuntimeEvent;
type PalletInfo = PalletInfo;
type OnSetCode = ();
type Block = Block;
type BlockHashCount = ConstU64<10>;
}
impl module1::Config<module1::Instance1> for Runtime {
type RuntimeEvent = RuntimeEvent;
}
impl module1::Config<module1::Instance2> for Runtime {
type RuntimeEvent = RuntimeEvent;
}
impl module1::Config<module1::Instance3> for Runtime {
type RuntimeEvent = RuntimeEvent;
}
impl module1::Config<module1::Instance4> for Runtime {
type RuntimeEvent = RuntimeEvent;
}
impl module1::Config<module1::Instance5> for Runtime {
type RuntimeEvent = RuntimeEvent;
}
impl module1::Config<module1::Instance6> for Runtime {
type RuntimeEvent = RuntimeEvent;
}
impl module1::Config<module1::Instance7> for Runtime {
type RuntimeEvent = RuntimeEvent;
}
impl module1::Config<module1::Instance8> for Runtime {
type RuntimeEvent = RuntimeEvent;
}
impl module1::Config<module1::Instance9> for Runtime {
type RuntimeEvent = RuntimeEvent;
}
impl module2::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
}
impl nested::module3::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
}
impl module3::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
}
fn test_pub() -> AccountId {
AccountId::from_raw([0; 32])
}
#[test]
fn check_modules_error_type() {
sp_io::TestExternalities::default().execute_with(|| {
assert_eq!(
Module1_1::fail(frame_system::Origin::<Runtime>::Root.into()),
Err(DispatchError::Module(ModuleError {
index: 31,
error: [0; 4],
message: Some("Something")
})),
);
assert_eq!(
Module2::fail(frame_system::Origin::<Runtime>::Root.into()),
Err(DispatchError::Module(ModuleError {
index: 32,
error: [0; 4],
message: Some("Something")
})),
);
assert_eq!(
Module1_2::fail(frame_system::Origin::<Runtime>::Root.into()),
Err(DispatchError::Module(ModuleError {
index: 33,
error: [0; 4],
message: Some("Something")
})),
);
assert_eq!(
NestedModule3::fail(frame_system::Origin::<Runtime>::Root.into()),
Err(DispatchError::Module(ModuleError {
index: 34,
error: [0; 4],
message: Some("Something")
})),
);
assert_eq!(
Module1_3::fail(frame_system::Origin::<Runtime>::Root.into()),
Err(DispatchError::Module(ModuleError {
index: 6,
error: [0; 4],
message: Some("Something")
})),
);
assert_eq!(
Module1_4::fail(frame_system::Origin::<Runtime>::Root.into()),
Err(DispatchError::Module(ModuleError {
index: 3,
error: [0; 4],
message: Some("Something")
})),
);
assert_eq!(
Module1_5::fail(frame_system::Origin::<Runtime>::Root.into()),
Err(DispatchError::Module(ModuleError {
index: 4,
error: [0; 4],
message: Some("Something")
})),
);
assert_eq!(
Module1_6::fail(frame_system::Origin::<Runtime>::Root.into()),
Err(DispatchError::Module(ModuleError {
index: 1,
error: [0; 4],
message: Some("Something")
})),
);
assert_eq!(
Module1_7::fail(frame_system::Origin::<Runtime>::Root.into()),
Err(DispatchError::Module(ModuleError {
index: 2,
error: [0; 4],
message: Some("Something")
})),
);
assert_eq!(
Module1_8::fail(frame_system::Origin::<Runtime>::Root.into()),
Err(DispatchError::Module(ModuleError {
index: 12,
error: [0; 4],
message: Some("Something")
})),
);
assert_eq!(
Module1_9::fail(frame_system::Origin::<Runtime>::Root.into()),
Err(DispatchError::Module(ModuleError {
index: 13,
error: [0; 4],
message: Some("Something")
})),
);
})
}
#[test]
fn integrity_test_works() {
__construct_runtime_integrity_test::runtime_integrity_tests();
assert_eq!(IntegrityTestExec::get(), 2);
}
#[test]
fn origin_codec() {
use codec::Encode;
let origin = OriginCaller::system(frame_system::RawOrigin::None);
assert_eq!(origin.encode()[0], 30);
let origin = OriginCaller::Module1_1(module1::Origin(Default::default()));
assert_eq!(origin.encode()[0], 31);
let origin = OriginCaller::Module2(module2::Origin);
assert_eq!(origin.encode()[0], 32);
let origin = OriginCaller::Module1_2(module1::Origin(Default::default()));
assert_eq!(origin.encode()[0], 33);
let origin = OriginCaller::NestedModule3(nested::module3::Origin);
assert_eq!(origin.encode()[0], 34);
let origin = OriginCaller::Module3(module3::Origin(Default::default()));
assert_eq!(origin.encode()[0], 35);
let origin = OriginCaller::Module1_6(module1::Origin(Default::default()));
assert_eq!(origin.encode()[0], 1);
let origin = OriginCaller::Module1_7(module1::Origin(Default::default()));
assert_eq!(origin.encode()[0], 2);
let origin = OriginCaller::Module1_8(module1::Origin(Default::default()));
assert_eq!(origin.encode()[0], 12);
let origin = OriginCaller::Module1_9(module1::Origin(Default::default()));
assert_eq!(origin.encode()[0], 13);
}
#[test]
fn event_codec() {
use codec::Encode;
let event =
frame_system::Event::<Runtime>::ExtrinsicSuccess { dispatch_info: Default::default() };
assert_eq!(RuntimeEvent::from(event).encode()[0], 30);
let event = module1::Event::<Runtime, module1::Instance1>::A(test_pub());
assert_eq!(RuntimeEvent::from(event).encode()[0], 31);
let event = module2::Event::A;
assert_eq!(RuntimeEvent::from(event).encode()[0], 32);
let event = module1::Event::<Runtime, module1::Instance2>::A(test_pub());
assert_eq!(RuntimeEvent::from(event).encode()[0], 33);
let event = nested::module3::Event::A;
assert_eq!(RuntimeEvent::from(event).encode()[0], 34);
let event = module3::Event::A;
assert_eq!(RuntimeEvent::from(event).encode()[0], 35);
let event = module1::Event::<Runtime, module1::Instance5>::A(test_pub());
assert_eq!(RuntimeEvent::from(event).encode()[0], 4);
let event = module1::Event::<Runtime, module1::Instance6>::A(test_pub());
assert_eq!(RuntimeEvent::from(event).encode()[0], 1);
let event = module1::Event::<Runtime, module1::Instance7>::A(test_pub());
assert_eq!(RuntimeEvent::from(event).encode()[0], 2);
let event = module1::Event::<Runtime, module1::Instance8>::A(test_pub());
assert_eq!(RuntimeEvent::from(event).encode()[0], 12);
let event = module1::Event::<Runtime, module1::Instance9>::A(test_pub());
assert_eq!(RuntimeEvent::from(event).encode()[0], 13);
}
#[test]
fn call_codec() {
use codec::Encode;
assert_eq!(RuntimeCall::System(frame_system::Call::remark { remark: vec![1] }).encode()[0], 30);
assert_eq!(RuntimeCall::Module1_1(module1::Call::fail {}).encode()[0], 31);
assert_eq!(RuntimeCall::Module2(module2::Call::fail {}).encode()[0], 32);
assert_eq!(RuntimeCall::Module1_2(module1::Call::fail {}).encode()[0], 33);
assert_eq!(RuntimeCall::NestedModule3(nested::module3::Call::fail {}).encode()[0], 34);
assert_eq!(RuntimeCall::Module3(module3::Call::fail {}).encode()[0], 35);
assert_eq!(RuntimeCall::Module1_4(module1::Call::fail {}).encode()[0], 3);
assert_eq!(RuntimeCall::Module1_6(module1::Call::fail {}).encode()[0], 1);
assert_eq!(RuntimeCall::Module1_7(module1::Call::fail {}).encode()[0], 2);
assert_eq!(RuntimeCall::Module1_8(module1::Call::fail {}).encode()[0], 12);
assert_eq!(RuntimeCall::Module1_9(module1::Call::fail {}).encode()[0], 13);
}
#[test]
fn call_compact_attr() {
use codec::Encode;
let call: module3::Call<Runtime> = module3::Call::aux_1 { data: 1 };
let encoded = call.encode();
assert_eq!(2, encoded.len());
assert_eq!(vec![1, 4], encoded);
let call: module3::Call<Runtime> = module3::Call::aux_2 { data: 1, data2: 2 };
let encoded = call.encode();
assert_eq!(6, encoded.len());
assert_eq!(vec![2, 1, 0, 0, 0, 8], encoded);
}
#[test]
fn call_encode_is_correct_and_decode_works() {
use codec::{Decode, Encode};
let call: module3::Call<Runtime> = module3::Call::fail {};
let encoded = call.encode();
assert_eq!(vec![0], encoded);
let decoded = module3::Call::<Runtime>::decode(&mut &encoded[..]).unwrap();
assert_eq!(decoded, call);
let call: module3::Call<Runtime> = module3::Call::aux_3 { data: 32, data2: "hello".into() };
let encoded = call.encode();
assert_eq!(vec![3, 32, 0, 0, 0, 20, 104, 101, 108, 108, 111], encoded);
let decoded = module3::Call::<Runtime>::decode(&mut &encoded[..]).unwrap();
assert_eq!(decoded, call);
}
#[test]
fn call_weight_should_attach_to_call_enum() {
use frame_support::{
dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays},
weights::Weight,
};
// operational.
assert_eq!(
module3::Call::<Runtime>::operational {}.get_dispatch_info(),
DispatchInfo {
weight: Weight::from_parts(5, 0),
class: DispatchClass::Operational,
pays_fee: Pays::Yes
},
);
// custom basic
assert_eq!(
module3::Call::<Runtime>::aux_4 {}.get_dispatch_info(),
DispatchInfo {
weight: Weight::from_parts(3, 0),
class: DispatchClass::Normal,
pays_fee: Pays::Yes
},
);
}
#[test]
fn call_name() {
use frame_support::traits::GetCallName;
let name = module3::Call::<Runtime>::aux_4 {}.get_call_name();
assert_eq!("aux_4", name);
}
#[test]
fn call_metadata() {
use frame_support::traits::{CallMetadata, GetCallMetadata};
let call = RuntimeCall::Module3(module3::Call::<Runtime>::aux_4 {});
let metadata = call.get_call_metadata();
let expected = CallMetadata { function_name: "aux_4".into(), pallet_name: "Module3".into() };
assert_eq!(metadata, expected);
}
#[test]
fn get_call_names() {
use frame_support::traits::GetCallName;
let call_names = module3::Call::<Runtime>::get_call_names();
assert_eq!(["fail", "aux_1", "aux_2", "aux_3", "aux_4", "operational"], call_names);
}
#[test]
fn get_module_names() {
use frame_support::traits::GetCallMetadata;
let module_names = RuntimeCall::get_module_names();
assert_eq!(
[
"System",
"Module1_1",
"Module2",
"Module1_2",
"NestedModule3",
"Module3",
"Module1_4",
"Module1_6",
"Module1_7",
"Module1_8",
"Module1_9",
],
module_names
);
}
#[test]
fn call_subtype_conversion() {
use frame_support::{dispatch::CallableCallFor, traits::IsSubType};
let call = RuntimeCall::Module3(module3::Call::<Runtime>::fail {});
let subcall: Option<&CallableCallFor<Module3, Runtime>> = call.is_sub_type();
let subcall_none: Option<&CallableCallFor<Module2, Runtime>> = call.is_sub_type();
assert_eq!(Some(&module3::Call::<Runtime>::fail {}), subcall);
assert_eq!(None, subcall_none);
let from = RuntimeCall::from(subcall.unwrap().clone());
assert_eq!(from, call);
}
#[test]
fn test_metadata() {
use frame_metadata::{
v14::{StorageEntryType::Plain, *},
*,
};
use scale_info::meta_type;
use sp_core::Encode;
use sp_metadata_ir::StorageEntryModifierIR::Optional;
fn maybe_docs(doc: Vec<&'static str>) -> Vec<&'static str> {
if cfg!(feature = "no-metadata-docs") {
vec![]
} else {
doc
}
}
let pallets = vec![
PalletMetadata {
name: "System",
storage: None,
calls: Some(meta_type::<frame_system::Call<Runtime>>().into()),
event: Some(meta_type::<frame_system::Event<Runtime>>().into()),
constants: vec![
PalletConstantMetadata {
name: "BlockWeights",
ty: meta_type::<BlockWeights>(),
value: BlockWeights::default().encode(),
docs: maybe_docs(vec![" Block & extrinsics weights: base values and limits."]),
},
PalletConstantMetadata {
name: "BlockLength",
ty: meta_type::<BlockLength>(),
value: BlockLength::default().encode(),
docs: maybe_docs(vec![" The maximum length of a block (in bytes)."]),
},
PalletConstantMetadata {
name: "BlockHashCount",
ty: meta_type::<u64>(),
value: 10u64.encode(),
docs: maybe_docs(vec![" Maximum number of block number to block hash mappings to keep (oldest pruned first)."]),
},
PalletConstantMetadata {
name: "DbWeight",
ty: meta_type::<RuntimeDbWeight>(),
value: RuntimeDbWeight::default().encode(),
docs: maybe_docs(vec![" The weight of runtime database operations the runtime can invoke.",]),
},
PalletConstantMetadata {
name: "Version",
ty: meta_type::<RuntimeVersion>(),
value: RuntimeVersion::default().encode(),
docs: maybe_docs(vec![ " Get the chain's current version."]),
},
PalletConstantMetadata {
name: "SS58Prefix",
ty: meta_type::<u16>(),
value: 0u16.encode(),
docs: maybe_docs(vec![
" The designated SS58 prefix of this chain.",
"",
" This replaces the \"ss58Format\" property declared in the chain spec. Reason is",
" that the runtime should know about the prefix in order to make use of it as",
" an identifier of the chain.",
]),
},
],
error: Some(meta_type::<frame_system::Error<Runtime>>().into()),
index: 30,
},
PalletMetadata {
name: "Module1_1",
storage: None,
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance1>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance1>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime>>().into()),
index: 31,
},
PalletMetadata {
name: "Module2",
storage: None,
calls: Some(meta_type::<module2::Call<Runtime>>().into()),
event: Some(meta_type::<module2::Event<Runtime>>().into()),
constants: vec![],
error: Some(meta_type::<module2::Error<Runtime>>().into()),
index: 32,
},
PalletMetadata {
name: "Module1_2",
storage: None,
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance2>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance2>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance2>>().into()),
index: 33,
},
PalletMetadata {
name: "NestedModule3",
storage: None,
calls: Some(meta_type::<nested::module3::Call<Runtime>>().into()),
event: Some(meta_type::<nested::module3::Event<Runtime>>().into()),
constants: vec![],
error: Some(meta_type::<nested::module3::Error<Runtime>>().into()),
index: 34,
},
PalletMetadata {
name: "Module3",
storage: Some(PalletStorageMetadata {
prefix: "Module3",
entries: vec![
StorageEntryMetadata {
name: "Storage",
modifier: Optional.into(),
ty: Plain(meta_type::<u32>().into()),
default: vec![0],
docs: vec![],
},
]
}),
calls: Some(meta_type::<module3::Call<Runtime>>().into()),
event: Some(meta_type::<module3::Event<Runtime>>().into()),
constants: vec![],
error: Some(meta_type::<module3::Error<Runtime>>().into()),
index: 35,
},
PalletMetadata {
name: "Module1_3",
storage: None,
calls: None,
event: Some(meta_type::<module1::Event<Runtime, module1::Instance3>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance3>>().into()),
index: 6,
},
PalletMetadata {
name: "Module1_4",
storage: None,
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance4>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance4>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance4>>().into()),
index: 3,
},
PalletMetadata {
name: "Module1_5",
storage: None,
calls: None,
event: Some(meta_type::<module1::Event<Runtime, module1::Instance5>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance5>>().into()),
index: 4,
},
PalletMetadata {
name: "Module1_6",
storage:None,
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance6>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance6>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance6>>().into()),
index: 1,
},
PalletMetadata {
name: "Module1_7",
storage: None,
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance7>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance7>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance7>>().into()),
index: 2,
},
PalletMetadata {
name: "Module1_8",
storage: None,
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance8>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance8>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance8>>().into()),
index: 12,
},
PalletMetadata {
name: "Module1_9",
storage: None,
calls: Some(meta_type::<module1::Call<Runtime, module1::Instance9>>().into()),
event: Some(meta_type::<module1::Event<Runtime, module1::Instance9>>().into()),
constants: vec![],
error: Some(meta_type::<module1::Error<Runtime, module1::Instance9>>().into()),
index: 13,
},
];
let extrinsic = ExtrinsicMetadata {
ty: meta_type::<UncheckedExtrinsic>(),
version: 4,
signed_extensions: vec![SignedExtensionMetadata {
identifier: "UnitTransactionExtension",
ty: meta_type::<()>(),
additional_signed: meta_type::<()>(),
}],
};
let expected_metadata: RuntimeMetadataPrefixed =
RuntimeMetadataLastVersion::new(pallets, extrinsic, meta_type::<Runtime>()).into();
let actual_metadata = Runtime::metadata();
pretty_assertions::assert_eq!(actual_metadata, expected_metadata);
}
#[test]
fn pallet_in_runtime_is_correct() {
assert_eq!(PalletInfo::index::<System>().unwrap(), 30);
assert_eq!(PalletInfo::name::<System>().unwrap(), "System");
assert_eq!(PalletInfo::module_name::<System>().unwrap(), "frame_system");
assert!(PalletInfo::crate_version::<System>().is_some());
assert_eq!(PalletInfo::index::<Module1_1>().unwrap(), 31);
assert_eq!(PalletInfo::name::<Module1_1>().unwrap(), "Module1_1");
assert_eq!(PalletInfo::module_name::<Module1_1>().unwrap(), "module1");
assert!(PalletInfo::crate_version::<Module1_1>().is_some());
assert_eq!(PalletInfo::index::<Module2>().unwrap(), 32);
assert_eq!(PalletInfo::name::<Module2>().unwrap(), "Module2");
assert_eq!(PalletInfo::module_name::<Module2>().unwrap(), "module2");
assert!(PalletInfo::crate_version::<Module2>().is_some());
assert_eq!(PalletInfo::index::<Module1_2>().unwrap(), 33);
assert_eq!(PalletInfo::name::<Module1_2>().unwrap(), "Module1_2");
assert_eq!(PalletInfo::module_name::<Module1_2>().unwrap(), "module1");
assert!(PalletInfo::crate_version::<Module1_2>().is_some());
assert_eq!(PalletInfo::index::<NestedModule3>().unwrap(), 34);
assert_eq!(PalletInfo::name::<NestedModule3>().unwrap(), "NestedModule3");
assert_eq!(PalletInfo::module_name::<NestedModule3>().unwrap(), "nested::module3");
assert!(PalletInfo::crate_version::<NestedModule3>().is_some());
assert_eq!(PalletInfo::index::<Module3>().unwrap(), 35);
assert_eq!(PalletInfo::name::<Module3>().unwrap(), "Module3");
assert_eq!(PalletInfo::module_name::<Module3>().unwrap(), "self::module3");
assert!(PalletInfo::crate_version::<Module3>().is_some());
assert_eq!(PalletInfo::index::<Module1_3>().unwrap(), 6);
assert_eq!(PalletInfo::name::<Module1_3>().unwrap(), "Module1_3");
assert_eq!(PalletInfo::module_name::<Module1_3>().unwrap(), "module1");
assert!(PalletInfo::crate_version::<Module1_3>().is_some());
assert_eq!(PalletInfo::index::<Module1_4>().unwrap(), 3);
assert_eq!(PalletInfo::name::<Module1_4>().unwrap(), "Module1_4");
assert_eq!(PalletInfo::module_name::<Module1_4>().unwrap(), "module1");
assert!(PalletInfo::crate_version::<Module1_4>().is_some());
assert_eq!(PalletInfo::index::<Module1_5>().unwrap(), 4);
assert_eq!(PalletInfo::name::<Module1_5>().unwrap(), "Module1_5");
assert_eq!(PalletInfo::module_name::<Module1_5>().unwrap(), "module1");
assert!(PalletInfo::crate_version::<Module1_5>().is_some());
assert_eq!(PalletInfo::index::<Module1_6>().unwrap(), 1);
assert_eq!(PalletInfo::name::<Module1_6>().unwrap(), "Module1_6");
assert_eq!(PalletInfo::module_name::<Module1_6>().unwrap(), "module1");
assert!(PalletInfo::crate_version::<Module1_6>().is_some());
assert_eq!(PalletInfo::index::<Module1_7>().unwrap(), 2);
assert_eq!(PalletInfo::name::<Module1_7>().unwrap(), "Module1_7");
assert_eq!(PalletInfo::module_name::<Module1_7>().unwrap(), "module1");
assert!(PalletInfo::crate_version::<Module1_7>().is_some());
assert_eq!(PalletInfo::index::<Module1_8>().unwrap(), 12);
assert_eq!(PalletInfo::name::<Module1_8>().unwrap(), "Module1_8");
assert_eq!(PalletInfo::module_name::<Module1_8>().unwrap(), "module1");
assert!(PalletInfo::crate_version::<Module1_8>().is_some());
assert_eq!(PalletInfo::index::<Module1_9>().unwrap(), 13);
assert_eq!(PalletInfo::name::<Module1_9>().unwrap(), "Module1_9");
assert_eq!(PalletInfo::module_name::<Module1_9>().unwrap(), "module1");
assert!(PalletInfo::crate_version::<Module1_9>().is_some());
}
#[test]
fn test_validate_unsigned() {
use frame_support::pallet_prelude::*;
let call = RuntimeCall::NestedModule3(nested::module3::Call::fail {});
let validity = Runtime::validate_unsigned(TransactionSource::Local, &call).unwrap_err();
assert_eq!(validity, TransactionValidityError::Invalid(InvalidTransaction::Call));
let call = RuntimeCall::Module3(module3::Call::fail {});
let validity = Runtime::validate_unsigned(TransactionSource::Local, &call).unwrap_err();
assert_eq!(
validity,
TransactionValidityError::Unknown(UnknownTransaction::NoUnsignedValidator)
);
}
@@ -0,0 +1,36 @@
// 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.
#[rustversion::attr(not(stable), ignore)]
#[cfg(not(feature = "disable-ui-tests"))]
#[test]
fn ui() {
// Only run the ui tests when `RUN_UI_TESTS` is set.
if std::env::var("RUN_UI_TESTS").is_err() {
return
}
// As trybuild is using `cargo check`, we don't need the real WASM binaries.
std::env::set_var("SKIP_WASM_BUILD", "1");
// Deny all warnings since we emit warnings as part of a Runtime's UI.
std::env::set_var("RUSTFLAGS", "--deny warnings");
let t = trybuild::TestCases::new();
t.compile_fail("tests/runtime_ui/*.rs");
t.pass("tests/runtime_ui/pass/*.rs");
}
@@ -0,0 +1,21 @@
// 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.
#[frame_support::runtime]
fn construct_runtime() {}
fn main() {}
@@ -0,0 +1,5 @@
error: expected `mod`
--> tests/runtime_ui/can_only_be_attached_to_mod.rs:19:1
|
19 | fn construct_runtime() {}
| ^^
@@ -0,0 +1,43 @@
// 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.
#[frame_support::pallet]
mod pallet {
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::call]
impl<T: Config> Pallet<T> {}
}
#[frame_support::runtime]
mod runtime {
#[runtime::runtime]
#[runtime::derive(RuntimeCall)]
pub struct Runtime;
#[runtime::pallet_index(0)]
pub type System = frame_system;
#[runtime::pallet_index(0)]
pub type Pallet = pallet;
}
fn main() {}
@@ -0,0 +1,11 @@
error: Pallet indices are conflicting: Both pallets System and Pallet are at index 0
--> tests/runtime_ui/conflicting_pallet_index.rs:37:14
|
37 | pub type System = frame_system;
| ^^^^^^
error: Pallet indices are conflicting: Both pallets System and Pallet are at index 0
--> tests/runtime_ui/conflicting_pallet_index.rs:40:14
|
40 | pub type Pallet = pallet;
| ^^^^^^
@@ -0,0 +1,43 @@
// 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.
#[frame_support::pallet]
mod pallet {
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::call]
impl<T: Config> Pallet<T> {}
}
#[frame_support::runtime]
mod runtime {
#[runtime::runtime]
#[runtime::derive(RuntimeCall)]
pub struct Runtime;
#[runtime::pallet_index(0)]
pub type System = frame_system;
#[runtime::pallet_index(1)]
pub type System = pallet;
}
fn main() {}
@@ -0,0 +1,11 @@
error: Two pallets with the same name!
--> tests/runtime_ui/conflicting_pallet_name.rs:37:14
|
37 | pub type System = frame_system;
| ^^^^^^
error: Two pallets with the same name!
--> tests/runtime_ui/conflicting_pallet_name.rs:40:14
|
40 | pub type System = pallet;
| ^^^^^^
@@ -0,0 +1,23 @@
// 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.
#[frame_support::runtime(dummy)]
mod runtime {
}
fn main() {}
@@ -0,0 +1,5 @@
error: 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)].
--> tests/runtime_ui/invalid_attribute.rs:18:26
|
18 | #[frame_support::runtime(dummy)]
| ^^^^^
@@ -0,0 +1,28 @@
// 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.
#[frame_support::runtime]
mod runtime {
#[runtime::runtime]
#[runtime::derive(RuntimeCall)]
pub struct Runtime;
#[runtime::pallet_index("0")]
pub type System = frame_system;
}
fn main() {}
@@ -0,0 +1,5 @@
error: expected integer literal
--> tests/runtime_ui/invalid_pallet_index.rs:24:29
|
24 | #[runtime::pallet_index("0")]
| ^^^
@@ -0,0 +1,25 @@
// 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.
#[frame_support::runtime]
mod runtime {
#[runtime::runtime]
#[runtime::derive(RuntimeInfo)]
pub struct Runtime;
}
fn main() {}
@@ -0,0 +1,5 @@
error: expected one of: `RuntimeCall`, `RuntimeEvent`, `RuntimeError`, `RuntimeOrigin`, `RuntimeFreezeReason`, `RuntimeHoldReason`, `RuntimeSlashReason`, `RuntimeLockId`, `RuntimeTask`
--> tests/runtime_ui/invalid_runtime_type_derive.rs:21:23
|
21 | #[runtime::derive(RuntimeInfo)]
| ^^^^^^^^^^^
@@ -0,0 +1,21 @@
// 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.
#[frame_support::runtime]
mod runtime {}
fn main() {}
@@ -0,0 +1,5 @@
error: Missing Runtime. Please add a struct inside the module and annotate it with `#[runtime::runtime]`
--> tests/runtime_ui/missing_runtime.rs:19:1
|
19 | mod runtime {}
| ^^^
@@ -0,0 +1,24 @@
// 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.
#[frame_support::runtime]
mod runtime {
#[runtime::runtime]
pub struct Runtime;
}
fn main() {}
@@ -0,0 +1,5 @@
error: Missing Runtime Types. Please annotate the runtime struct with `#[runtime::derive]`
--> tests/runtime_ui/missing_runtime_types_derive.rs:19:1
|
19 | mod runtime {
| ^^^
@@ -0,0 +1,25 @@
// 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.
#[frame_support::runtime]
mod runtime {
#[runtime::runtime]
#[runtime::derive(RuntimeCall)]
pub struct Runtime;
}
fn main() {}
@@ -0,0 +1,5 @@
error: `System` pallet declaration is missing. Please add this line: `pub type System = frame_system;`
--> tests/runtime_ui/missing_system_pallet.rs:19:5
|
19 | mod runtime {
| ^^^^^^^
@@ -0,0 +1,37 @@
// 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 frame_support::derive_impl;
pub type Block = frame_system::mocking::MockBlock<Runtime>;
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
impl frame_system::Config for Runtime {
type Block = Block;
}
#[frame_support::runtime]
mod runtime {
#[runtime::runtime]
#[runtime::derive(RuntimeCall, RuntimeEvent, RuntimeOrigin, RuntimeError, RuntimeTask)]
pub struct Runtime;
#[runtime::pallet_index(0)]
pub type System = frame_system;
}
fn main() {}
@@ -0,0 +1,24 @@
// 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.
#[frame_support::runtime]
mod runtime {
#[runtime::runtime]
pub enum Runtime {}
}
fn main() {}
@@ -0,0 +1,5 @@
error: Invalid runtime::runtime, expected struct definition
--> tests/runtime_ui/runtime_struct.rs:21:5
|
21 | pub enum Runtime {}
| ^^^
+1 -1
View File
@@ -25,7 +25,7 @@ scale-info = { version = "2.10.0", default-features = false, features = [
] }
# frame
frame-support = { path = "../../../substrate/frame/support", default-features = false }
frame-support = { path = "../../../substrate/frame/support", default-features = false, features = ["experimental"] }
frame-system = { path = "../../../substrate/frame/system", default-features = false }
frame-try-runtime = { path = "../../../substrate/frame/try-runtime", default-features = false, optional = true }
frame-executive = { path = "../../../substrate/frame/executive", default-features = false }
+40 -13
View File
@@ -253,20 +253,47 @@ impl pallet_template::Config for Runtime {
}
// Create the runtime by composing the FRAME pallets that were previously configured.
construct_runtime!(
pub enum Runtime {
System: frame_system,
Timestamp: pallet_timestamp,
Aura: pallet_aura,
Grandpa: pallet_grandpa,
Balances: pallet_balances,
TransactionPayment: pallet_transaction_payment,
Sudo: pallet_sudo,
#[frame_support::runtime]
mod runtime {
#[runtime::runtime]
#[runtime::derive(
RuntimeCall,
RuntimeEvent,
RuntimeError,
RuntimeOrigin,
RuntimeFreezeReason,
RuntimeHoldReason,
RuntimeSlashReason,
RuntimeLockId,
RuntimeTask
)]
pub struct Runtime;
// Include the custom logic from the pallet-template in the runtime.
TemplateModule: pallet_template,
}
);
#[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;
}
/// The address format for describing accounts.
pub type Address = sp_runtime::MultiAddress<AccountId, ()>;