diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/config.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/config.rs new file mode 100644 index 0000000000000000000000000000000000000000..93d4a868b78478e17bb2a6252cc0373b254da437 --- /dev/null +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/config.rs @@ -0,0 +1,123 @@ +// This file is part of Substrate. + +// Copyright (C) 2021 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::Pallet; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::Ident; + +pub fn expand_outer_config( + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream, +) -> TokenStream { + let mut types = TokenStream::new(); + let mut fields = TokenStream::new(); + let mut build_storage_calls = TokenStream::new(); + + for decl in pallet_decls { + if let Some(pallet_entry) = decl.find_part("Config") { + let config = format_ident!("{}Config", decl.name); + let mod_name = decl.pallet.mod_name(); + let field_name = if let Some(inst) = decl.instance.as_ref() { + format_ident!("{}_{}", mod_name, inst) + } else { + mod_name + }; + let part_is_generic = !pallet_entry.generics.params.is_empty(); + + types.extend(expand_config_types(runtime, decl, &config, part_is_generic)); + fields.extend(quote!(pub #field_name: #config,)); + build_storage_calls.extend(expand_config_build_storage_call(scrate, runtime, decl, &field_name)); + } + } + + quote!{ + #types + + #[cfg(any(feature = "std", test))] + use #scrate::serde as __genesis_config_serde_import__; + #[cfg(any(feature = "std", test))] + #[derive(#scrate::serde::Serialize, #scrate::serde::Deserialize, Default)] + #[serde(rename_all = "camelCase")] + #[serde(deny_unknown_fields)] + #[serde(crate = "__genesis_config_serde_import__")] + #[allow(non_snake_case)] + pub struct GenesisConfig { + #fields + } + + #[cfg(any(feature = "std", test))] + impl #scrate::sp_runtime::BuildStorage for GenesisConfig { + fn assimilate_storage( + &self, + storage: &mut #scrate::sp_runtime::Storage, + ) -> std::result::Result<(), String> { + #build_storage_calls + + #scrate::BasicExternalities::execute_with_storage(storage, || { + <AllPalletsWithSystem as #scrate::traits::OnGenesis>::on_genesis(); + }); + + Ok(()) + } + } + } +} + +fn expand_config_types( + runtime: &Ident, + decl: &Pallet, + config: &Ident, + part_is_generic: bool, +) -> TokenStream { + let path = &decl.pallet; + + match (decl.instance.as_ref(), part_is_generic) { + (Some(inst), true) => quote!{ + #[cfg(any(feature = "std", test))] + pub type #config = #path::GenesisConfig<#runtime, #path::#inst>; + }, + (None, true) => quote!{ + #[cfg(any(feature = "std", test))] + pub type #config = #path::GenesisConfig<#runtime>; + }, + (_, false) => quote!{ + #[cfg(any(feature = "std", test))] + pub type #config = #path::GenesisConfig; + }, + } +} + +fn expand_config_build_storage_call( + scrate: &TokenStream, + runtime: &Ident, + decl: &Pallet, + field_name: &Ident, +) -> TokenStream { + let path = &decl.pallet; + let instance = if let Some(inst) = decl.instance.as_ref() { + quote!(#path::#inst) + } else { + quote!(#path::__InherentHiddenInstance) + }; + + quote!{ + #scrate::sp_runtime::BuildModuleGenesisStorage:: + <#runtime, #instance>::build_module_genesis_storage(&self.#field_name, storage)?; + } +} diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/event.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/event.rs new file mode 100644 index 0000000000000000000000000000000000000000..c2c905e50ff8de36e764efcc418e1b360c1b5d12 --- /dev/null +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/event.rs @@ -0,0 +1,146 @@ +// This file is part of Substrate. + +// Copyright (C) 2021 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::{Pallet, parse::PalletPath}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::{Generics, Ident}; + +pub fn expand_outer_event( + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream, +) -> syn::Result<TokenStream> { + let mut event_variants = TokenStream::new(); + let mut event_conversions = TokenStream::new(); + let mut events_metadata = TokenStream::new(); + + for pallet_decl in pallet_decls { + if let Some(pallet_entry) = pallet_decl.find_part("Event") { + let path = &pallet_decl.pallet; + let index = pallet_decl.index; + let instance = pallet_decl.instance.as_ref(); + let generics = &pallet_entry.generics; + + if instance.is_some() && generics.params.is_empty() { + let msg = format!( + "Instantiable pallet with no generic `Event` cannot \ + be constructed: pallet `{}` must have generic `Event`", + pallet_decl.name, + ); + return Err(syn::Error::new(pallet_decl.name.span(), msg)); + } + + let part_is_generic = !generics.params.is_empty(); + let pallet_event = match (instance, part_is_generic) { + (Some(inst), true) => quote!(#path::Event::<#runtime, #path::#inst>), + (Some(inst), false) => quote!(#path::Event::<#path::#inst>), + (None, true) => quote!(#path::Event::<#runtime>), + (None, false) => quote!(#path::Event), + }; + + event_variants.extend(expand_event_variant(runtime, path, index, instance, generics)); + event_conversions.extend(expand_event_conversion(scrate, path, instance, &pallet_event)); + events_metadata.extend(expand_event_metadata(scrate, path, &pallet_event)); + } + } + + Ok(quote!{ + #[derive( + Clone, PartialEq, Eq, + #scrate::codec::Encode, + #scrate::codec::Decode, + #scrate::RuntimeDebug, + )] + #[allow(non_camel_case_types)] + pub enum Event { + #event_variants + } + + #event_conversions + }) +} + +fn expand_event_variant( + runtime: &Ident, + path: &PalletPath, + index: u8, + instance: Option<&Ident>, + generics: &Generics, +) -> TokenStream { + let part_is_generic = !generics.params.is_empty(); + let mod_name = &path.mod_name(); + + match (instance, part_is_generic) { + (Some(inst), true) => { + let variant = format_ident!("{}_{}", mod_name, inst); + quote!(#[codec(index = #index)] #variant(#path::Event<#runtime, #path::#inst>),) + } + (Some(inst), false) => { + let variant = format_ident!("{}_{}", mod_name, inst); + quote!(#[codec(index = #index)] #variant(#path::Event<#path::#inst>),) + } + (None, true) => { + quote!(#[codec(index = #index)] #mod_name(#path::Event<#runtime>),) + } + (None, false) => { + quote!(#[codec(index = #index)] #mod_name(#path::Event),) + } + } +} + +fn expand_event_conversion( + scrate: &TokenStream, + path: &PalletPath, + instance: Option<&Ident>, + pallet_event: &TokenStream, +) -> TokenStream { + let mod_name = path.mod_name(); + let variant = if let Some(inst) = instance { + format_ident!("{}_{}", mod_name, inst) + } else { + mod_name + }; + + quote!{ + impl From<#pallet_event> for Event { + fn from(x: #pallet_event) -> Self { + Event::#variant(x) + } + } + impl #scrate::sp_std::convert::TryInto<#pallet_event> for Event { + type Error = (); + + fn try_into(self) -> #scrate::sp_std::result::Result<#pallet_event, Self::Error> { + match self { + Self::#variant(evt) => Ok(evt), + _ => Err(()), + } + } + } + } +} + +fn expand_event_metadata( + scrate: &TokenStream, + path: &PalletPath, + pallet_event: &TokenStream, +) -> TokenStream { + let mod_name = path.mod_name(); + + quote!{(stringify!(#mod_name), #scrate::event::FnEncode(#pallet_event::metadata)),} +} diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs new file mode 100644 index 0000000000000000000000000000000000000000..cbabec73d3a6f9c4baccf9a692f54dd8cef61994 --- /dev/null +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs @@ -0,0 +1,190 @@ +// This file is part of Substrate. + +// Copyright (C) 2021 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 proc_macro2::TokenStream; +use crate::construct_runtime::Pallet; +use syn::{Ident, TypePath}; +use quote::quote; + +pub fn expand_runtime_metadata( + runtime: &Ident, + pallet_declarations: &[Pallet], + scrate: &TokenStream, + extrinsic: &TypePath, +) -> TokenStream { + let modules = pallet_declarations + .iter() + .filter_map(|pallet_declaration| { + pallet_declaration.find_part("Pallet").map(|_| { + let filtered_names: Vec<_> = pallet_declaration + .pallet_parts() + .iter() + .filter(|part| part.name() != "Pallet") + .map(|part| part.name()) + .collect(); + (pallet_declaration, filtered_names) + }) + }) + .map(|(decl, filtered_names)| { + let name = &decl.name; + let index = &decl.index; + let storage = expand_pallet_metadata_storage(&filtered_names, runtime, scrate, decl); + let calls = expand_pallet_metadata_calls(&filtered_names, runtime, scrate, decl); + let event = expand_pallet_metadata_events(&filtered_names, runtime, scrate, decl); + let constants = expand_pallet_metadata_constants(runtime, scrate, decl); + let errors = expand_pallet_metadata_errors(runtime, scrate, decl); + + quote!{ + #scrate::metadata::ModuleMetadata { + name: #scrate::metadata::DecodeDifferent::Encode(stringify!(#name)), + index: #index, + storage: #storage, + calls: #calls, + event: #event, + constants: #constants, + errors: #errors, + } + } + }) + .collect::<Vec<_>>(); + + quote!{ + impl #runtime { + pub fn metadata() -> #scrate::metadata::RuntimeMetadataPrefixed { + #scrate::metadata::RuntimeMetadataLastVersion { + modules: #scrate::metadata::DecodeDifferent::Encode(&[ #(#modules),* ]), + extrinsic: #scrate::metadata::ExtrinsicMetadata { + version: <#extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata>::VERSION, + signed_extensions: < + < + #extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata + >::SignedExtensions as #scrate::sp_runtime::traits::SignedExtension + >::identifier() + .into_iter() + .map(#scrate::metadata::DecodeDifferent::Encode) + .collect(), + }, + }.into() + } + } + } +} + +fn expand_pallet_metadata_storage( + filtered_names: &[&'static str], + runtime: &Ident, + scrate: &TokenStream, + decl: &Pallet, +) -> TokenStream { + if filtered_names.contains(&"Storage") { + let instance = decl.instance.as_ref().into_iter(); + let path = &decl.pallet; + + quote!{ + Some(#scrate::metadata::DecodeDifferent::Encode( + #scrate::metadata::FnEncode( + #path::Pallet::<#runtime #(, #path::#instance)*>::storage_metadata + ) + )) + } + } else { + quote!(None) + } +} + +fn expand_pallet_metadata_calls( + filtered_names: &[&'static str], + runtime: &Ident, + scrate: &TokenStream, + decl: &Pallet, +) -> TokenStream { + if filtered_names.contains(&"Call") { + let instance = decl.instance.as_ref().into_iter(); + let path = &decl.pallet; + + quote!{ + Some(#scrate::metadata::DecodeDifferent::Encode( + #scrate::metadata::FnEncode( + #path::Pallet::<#runtime #(, #path::#instance)*>::call_functions + ) + )) + } + } else { + quote!(None) + } +} + +fn expand_pallet_metadata_events( + filtered_names: &[&'static str], + runtime: &Ident, + scrate: &TokenStream, + decl: &Pallet, +) -> TokenStream { + if filtered_names.contains(&"Event") { + let path = &decl.pallet; + let part_is_generic = + !decl.find_part("Event").expect("Event part exists; qed").generics.params.is_empty(); + let pallet_event = match (decl.instance.as_ref(), part_is_generic) { + (Some(inst), true) => quote!(#path::Event::<#runtime, #path::#inst>), + (Some(inst), false) => quote!(#path::Event::<#path::#inst>), + (None, true) => quote!(#path::Event::<#runtime>), + (None, false) => quote!(#path::Event), + }; + + quote!{ + Some(#scrate::metadata::DecodeDifferent::Encode( + #scrate::metadata::FnEncode(#pallet_event::metadata) + )) + } + } else { + quote!(None) + } +} + +fn expand_pallet_metadata_constants( + runtime: &Ident, + scrate: &TokenStream, + decl: &Pallet, +) -> TokenStream { + let path = &decl.pallet; + let instance = decl.instance.as_ref().into_iter(); + + quote!{ + #scrate::metadata::DecodeDifferent::Encode( + #scrate::metadata::FnEncode( + #path::Pallet::<#runtime #(, #path::#instance)*>::module_constants_metadata + ) + ) + } +} + +fn expand_pallet_metadata_errors( + runtime: &Ident, + scrate: &TokenStream, + decl: &Pallet, +) -> TokenStream { + let path = &decl.pallet; + let instance = decl.instance.as_ref().into_iter(); + + quote!{ + #scrate::metadata::DecodeDifferent::Encode( + #scrate::metadata::FnEncode( + <#path::Pallet::<#runtime #(, #path::#instance)*> as #scrate::metadata::ModuleErrorMetadata>::metadata + ) + ) + } +} diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/mod.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..ab2242ba0546ec5bb438e8ea885a7d2be41c82a3 --- /dev/null +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/mod.rs @@ -0,0 +1,26 @@ +// This file is part of Substrate. + +// Copyright (C) 2021 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 + +mod config; +mod event; +mod metadata; +mod origin; + +pub use config::expand_outer_config; +pub use event::expand_outer_event; +pub use metadata::expand_runtime_metadata; +pub use origin::expand_outer_origin; diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs new file mode 100644 index 0000000000000000000000000000000000000000..8ebce237480c856b3e80c1a90fe7f082f65489e0 --- /dev/null +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs @@ -0,0 +1,341 @@ +// This file is part of Substrate. + +// Copyright (C) 2021 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::PalletPath, Pallet, SYSTEM_PALLET_NAME}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::{token, Ident, Generics}; + +pub fn expand_outer_origin( + runtime: &Ident, + pallets: &[Pallet], + pallets_token: token::Brace, + scrate: &TokenStream, +) -> syn::Result<TokenStream> { + let system_pallet = pallets.iter() + .find(|decl| decl.name == SYSTEM_PALLET_NAME) + .ok_or_else(|| syn::Error::new( + pallets_token.span, + "`System` pallet declaration is missing. \ + Please add this line: `System: frame_system::{Pallet, Call, Storage, Config, Event<T>},`", + ))?; + + let mut caller_variants = TokenStream::new(); + let mut pallet_conversions = TokenStream::new(); + + for pallet_decl in pallets.iter().filter(|pallet| pallet.name != SYSTEM_PALLET_NAME) { + if let Some(pallet_entry) = pallet_decl.find_part("Origin") { + let path = &pallet_decl.pallet; + let instance = pallet_decl.instance.as_ref(); + let index = pallet_decl.index; + let generics = &pallet_entry.generics; + + if instance.is_some() && generics.params.is_empty() { + let msg = format!( + "Instantiable pallet with no generic `Origin` cannot \ + be constructed: pallet `{}` must have generic `Origin`", + pallet_decl.name + ); + return Err(syn::Error::new(pallet_decl.name.span(), msg)); + } + + caller_variants.extend( + expand_origin_caller_variant(runtime, path, index, instance, generics), + ); + pallet_conversions.extend( + expand_origin_pallet_conversions(scrate, runtime, path, instance, generics), + ); + } + } + + let system_path = &system_pallet.pallet; + let system_index = system_pallet.index; + + Ok(quote!{ + // WARNING: All instance must hold the filter `frame_system::Config::BaseCallFilter`, except + // when caller is system Root. One can use `OriginTrait::reset_filter` to do so. + #[derive(Clone)] + pub struct Origin { + caller: OriginCaller, + filter: #scrate::sp_std::rc::Rc<Box<dyn Fn(&<#runtime as #system_path::Config>::Call) -> bool>>, + } + + #[cfg(not(feature = "std"))] + impl #scrate::sp_std::fmt::Debug for Origin { + fn fmt( + &self, + fmt: &mut #scrate::sp_std::fmt::Formatter, + ) -> #scrate::sp_std::result::Result<(), #scrate::sp_std::fmt::Error> { + fmt.write_str("<wasm:stripped>") + } + } + + #[cfg(feature = "std")] + impl #scrate::sp_std::fmt::Debug for Origin { + fn fmt( + &self, + fmt: &mut #scrate::sp_std::fmt::Formatter, + ) -> #scrate::sp_std::result::Result<(), #scrate::sp_std::fmt::Error> { + fmt.debug_struct("Origin") + .field("caller", &self.caller) + .field("filter", &"[function ptr]") + .finish() + } + } + + impl #scrate::traits::OriginTrait for Origin { + type Call = <#runtime as #system_path::Config>::Call; + type PalletsOrigin = OriginCaller; + type AccountId = <#runtime as #system_path::Config>::AccountId; + + fn add_filter(&mut self, filter: impl Fn(&Self::Call) -> bool + 'static) { + let f = self.filter.clone(); + + self.filter = #scrate::sp_std::rc::Rc::new(Box::new(move |call| { + f(call) && filter(call) + })); + } + + fn reset_filter(&mut self) { + let filter = < + <#runtime as #system_path::Config>::BaseCallFilter + as #scrate::traits::Filter<<#runtime as #system_path::Config>::Call> + >::filter; + + self.filter = #scrate::sp_std::rc::Rc::new(Box::new(filter)); + } + + fn set_caller_from(&mut self, other: impl Into<Self>) { + self.caller = other.into().caller; + } + + fn filter_call(&self, call: &Self::Call) -> bool { + (self.filter)(call) + } + + fn caller(&self) -> &Self::PalletsOrigin { + &self.caller + } + + fn try_with_caller<R>( + mut self, + f: impl FnOnce(Self::PalletsOrigin) -> Result<R, Self::PalletsOrigin>, + ) -> Result<R, Self> { + match f(self.caller) { + Ok(r) => Ok(r), + Err(caller) => { self.caller = caller; Err(self) } + } + } + + /// Create with system none origin and `frame-system::Config::BaseCallFilter`. + fn none() -> Self { + #system_path::RawOrigin::None.into() + } + /// Create with system root origin and no filter. + fn root() -> Self { + #system_path::RawOrigin::Root.into() + } + /// Create with system signed origin and `frame-system::Config::BaseCallFilter`. + fn signed(by: <#runtime as #system_path::Config>::AccountId) -> Self { + #system_path::RawOrigin::Signed(by).into() + } + } + + #[derive(Clone, PartialEq, Eq, #scrate::RuntimeDebug, #scrate::codec::Encode, #scrate::codec::Decode)] + #[allow(non_camel_case_types)] + pub enum OriginCaller { + #[codec(index = #system_index)] + system(#system_path::Origin<#runtime>), + #caller_variants + #[allow(dead_code)] + Void(#scrate::Void) + } + + // For backwards compatibility and ease of accessing these functions. + #[allow(dead_code)] + impl Origin { + /// Create with system none origin and `frame-system::Config::BaseCallFilter`. + pub fn none() -> Self { + <Origin as #scrate::traits::OriginTrait>::none() + } + /// Create with system root origin and no filter. + pub fn root() -> Self { + <Origin as #scrate::traits::OriginTrait>::root() + } + /// Create with system signed origin and `frame-system::Config::BaseCallFilter`. + pub fn signed(by: <#runtime as #system_path::Config>::AccountId) -> Self { + <Origin as #scrate::traits::OriginTrait>::signed(by) + } + } + + impl From<#system_path::Origin<#runtime>> for OriginCaller { + fn from(x: #system_path::Origin<#runtime>) -> Self { + OriginCaller::system(x) + } + } + + impl #scrate::sp_std::convert::TryFrom<OriginCaller> for #system_path::Origin<#runtime> { + type Error = OriginCaller; + fn try_from(x: OriginCaller) + -> #scrate::sp_std::result::Result<#system_path::Origin<#runtime>, OriginCaller> + { + if let OriginCaller::system(l) = x { + Ok(l) + } else { + Err(x) + } + } + } + + impl From<#system_path::Origin<#runtime>> for Origin { + /// Convert to runtime origin: + /// * root origin is built with no filter + /// * others use `frame-system::Config::BaseCallFilter` + fn from(x: #system_path::Origin<#runtime>) -> Self { + let o: OriginCaller = x.into(); + o.into() + } + } + + impl From<OriginCaller> for Origin { + fn from(x: OriginCaller) -> Self { + let mut o = Origin { + caller: x, + filter: #scrate::sp_std::rc::Rc::new(Box::new(|_| true)), + }; + + // Root has no filter + if !matches!(o.caller, OriginCaller::system(#system_path::Origin::<#runtime>::Root)) { + #scrate::traits::OriginTrait::reset_filter(&mut o); + } + + o + } + } + + impl Into<#scrate::sp_std::result::Result<#system_path::Origin<#runtime>, Origin>> for Origin { + /// NOTE: converting to pallet origin loses the origin filter information. + fn into(self) -> #scrate::sp_std::result::Result<#system_path::Origin<#runtime>, Self> { + if let OriginCaller::system(l) = self.caller { + Ok(l) + } else { + Err(self) + } + } + } + impl From<Option<<#runtime as #system_path::Config>::AccountId>> for Origin { + /// Convert to runtime origin with caller being system signed or none and use filter + /// `frame-system::Config::BaseCallFilter`. + fn from(x: Option<<#runtime as #system_path::Config>::AccountId>) -> Self { + <#system_path::Origin<#runtime>>::from(x).into() + } + } + + #pallet_conversions + }) +} + +fn expand_origin_caller_variant( + runtime: &Ident, + path: &PalletPath, + index: u8, + instance: Option<&Ident>, + generics: &Generics, +) -> TokenStream { + let part_is_generic = !generics.params.is_empty(); + let mod_name = &path.mod_name(); + + match (instance, part_is_generic) { + (Some(inst), true) => { + let variant = format_ident!("{}_{}", mod_name, inst); + quote!(#[codec(index = #index)] #variant(#path::Origin<#runtime, #path::#inst>),) + } + (Some(inst), false) => { + let variant = format_ident!("{}_{}", mod_name, inst); + quote!(#[codec(index = #index)] #variant(#path::Origin<#path::#inst>),) + } + (None, true) => { + quote!(#[codec(index = #index)] #mod_name(#path::Origin<#runtime>),) + } + (None, false) => { + quote!(#[codec(index = #index)] #mod_name(#path::Origin),) + } + } +} + +fn expand_origin_pallet_conversions( + scrate: &TokenStream, + runtime: &Ident, + path: &PalletPath, + instance: Option<&Ident>, + generics: &Generics, +) -> TokenStream { + let mod_name = path.mod_name(); + let variant = if let Some(inst) = instance { + format_ident!("{}_{}", mod_name, inst) + } else { + mod_name + }; + + let part_is_generic = !generics.params.is_empty(); + let pallet_origin = match (instance, part_is_generic) { + (Some(inst), true) => quote!(#path::Origin<#runtime, #path::#inst>), + (Some(inst), false) => quote!(#path::Origin<#path::#inst>), + (None, true) => quote!(#path::Origin<#runtime>), + (None, false) => quote!(#path::Origin), + }; + + quote!{ + impl From<#pallet_origin> for OriginCaller { + fn from(x: #pallet_origin) -> Self { + OriginCaller::#variant(x) + } + } + + impl From<#pallet_origin> for Origin { + /// Convert to runtime origin using `frame-system::Config::BaseCallFilter`. + fn from(x: #pallet_origin) -> Self { + let x: OriginCaller = x.into(); + x.into() + } + } + + impl Into<#scrate::sp_std::result::Result<#pallet_origin, Origin>> for Origin { + /// NOTE: converting to pallet origin loses the origin filter information. + fn into(self) -> #scrate::sp_std::result::Result<#pallet_origin, Self> { + if let OriginCaller::#variant(l) = self.caller { + Ok(l) + } else { + Err(self) + } + } + } + + impl #scrate::sp_std::convert::TryFrom<OriginCaller> for #pallet_origin { + type Error = OriginCaller; + fn try_from( + x: OriginCaller, + ) -> #scrate::sp_std::result::Result<#pallet_origin, OriginCaller> { + if let OriginCaller::#variant(l) = x { + Ok(l) + } else { + Err(x) + } + } + } + } +} diff --git a/substrate/frame/support/procedural/src/construct_runtime/mod.rs b/substrate/frame/support/procedural/src/construct_runtime/mod.rs index e14f90197f06026b7a8ac72e802c5bc0f30ee61d..a24168c463aa76a0ada359dfaaa9a27058927d18 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/mod.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/mod.rs @@ -15,15 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod expand; mod parse; use frame_support_procedural_tools::syn_ext as ext; use frame_support_procedural_tools::{generate_crate_access, generate_hidden_includes}; -use parse::{PalletDeclaration, RuntimeDefinition, WhereSection, PalletPart}; +use parse::{PalletDeclaration, PalletPart, PalletPath, RuntimeDefinition, WhereSection}; use proc_macro::TokenStream; -use proc_macro2::{TokenStream as TokenStream2}; +use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use syn::{Ident, Result, TypePath}; +use syn::{Ident, Result}; use std::collections::HashMap; /// The fixed name of the system pallet. @@ -34,7 +35,7 @@ const SYSTEM_PALLET_NAME: &str = "System"; pub struct Pallet { pub name: Ident, pub index: u8, - pub pallet: Ident, + pub pallet: PalletPath, pub instance: Option<Ident>, pub pallet_parts: Vec<PalletPart>, } @@ -134,38 +135,19 @@ fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result<TokenStream let pallets = complete_pallets(pallets.into_iter())?; - let system_pallet = pallets.iter() - .find(|decl| decl.name == SYSTEM_PALLET_NAME) - .ok_or_else(|| syn::Error::new( - pallets_token.span, - "`System` pallet declaration is missing. \ - Please add this line: `System: frame_system::{Pallet, Call, Storage, Config, Event<T>},`", - ))?; - 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 all_but_system_pallets = pallets.iter().filter(|pallet| pallet.name != SYSTEM_PALLET_NAME); - - let outer_event = decl_outer_event( - &name, - pallets.iter(), - &scrate, - )?; + let outer_event = expand::expand_outer_event(&name, &pallets, &scrate)?; - let outer_origin = decl_outer_origin( - &name, - all_but_system_pallets, - &system_pallet, - &scrate, - )?; + let outer_origin = expand::expand_outer_origin(&name, &pallets, pallets_token, &scrate)?; let all_pallets = decl_all_pallets(&name, pallets.iter()); let pallet_to_index = decl_pallet_runtime_setup(&pallets, &scrate); let dispatch = decl_outer_dispatch(&name, pallets.iter(), &scrate); - let metadata = decl_runtime_metadata(&name, pallets.iter(), &scrate, &unchecked_extrinsic); - let outer_config = decl_outer_config(&name, pallets.iter(), &scrate); + let metadata = expand::expand_runtime_metadata(&name, &pallets, &scrate, &unchecked_extrinsic); + let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); let inherent = decl_outer_inherent( &name, &block, @@ -262,85 +244,6 @@ fn decl_outer_inherent<'a>( ) } -fn decl_outer_config<'a>( - runtime: &'a Ident, - pallet_declarations: impl Iterator<Item = &'a Pallet>, - scrate: &'a TokenStream2, -) -> TokenStream2 { - let pallets_tokens = pallet_declarations - .filter_map(|pallet_declaration| { - pallet_declaration.find_part("Config").map(|part| { - let transformed_generics: Vec<_> = part - .generics - .params - .iter() - .map(|param| quote!(<#param>)) - .collect(); - (pallet_declaration, transformed_generics) - }) - }) - .map(|(pallet_declaration, generics)| { - let pallet = &pallet_declaration.pallet; - let name = Ident::new( - &format!("{}Config", pallet_declaration.name), - pallet_declaration.name.span(), - ); - let instance = pallet_declaration.instance.as_ref().into_iter(); - quote!( - #name => - #pallet #(#instance)* #(#generics)*, - ) - }); - quote!( - #scrate::impl_outer_config! { - pub struct GenesisConfig for #runtime where AllPalletsWithSystem = AllPalletsWithSystem { - #(#pallets_tokens)* - } - } - ) -} - -fn decl_runtime_metadata<'a>( - runtime: &'a Ident, - pallet_declarations: impl Iterator<Item = &'a Pallet>, - scrate: &'a TokenStream2, - extrinsic: &TypePath, -) -> TokenStream2 { - let pallets_tokens = pallet_declarations - .filter_map(|pallet_declaration| { - pallet_declaration.find_part("Pallet").map(|_| { - let filtered_names: Vec<_> = pallet_declaration - .pallet_parts() - .iter() - .filter(|part| part.name() != "Pallet") - .map(|part| part.ident()) - .collect(); - (pallet_declaration, filtered_names) - }) - }) - .map(|(pallet_declaration, filtered_names)| { - let pallet = &pallet_declaration.pallet; - let name = &pallet_declaration.name; - let instance = pallet_declaration - .instance - .as_ref() - .map(|name| quote!(<#name>)) - .into_iter(); - - let index = pallet_declaration.index; - - quote!( - #pallet::Pallet #(#instance)* as #name { index #index } with #(#filtered_names)*, - ) - }); - quote!( - #scrate::impl_runtime_metadata!{ - for #runtime with pallets where Extrinsic = #extrinsic - #(#pallets_tokens)* - } - ) -} - fn decl_outer_dispatch<'a>( runtime: &'a Ident, pallet_declarations: impl Iterator<Item = &'a Pallet>, @@ -349,7 +252,7 @@ fn decl_outer_dispatch<'a>( let pallets_tokens = pallet_declarations .filter(|pallet_declaration| pallet_declaration.exists_part("Call")) .map(|pallet_declaration| { - let pallet = &pallet_declaration.pallet; + let pallet = &pallet_declaration.pallet.inner.segments.last().unwrap(); let name = &pallet_declaration.name; let index = pallet_declaration.index; quote!(#[codec(index = #index)] #pallet::#name) @@ -364,82 +267,6 @@ fn decl_outer_dispatch<'a>( ) } -fn decl_outer_origin<'a>( - runtime_name: &'a Ident, - pallets_except_system: impl Iterator<Item = &'a Pallet>, - system_pallet: &'a Pallet, - scrate: &'a TokenStream2, -) -> syn::Result<TokenStream2> { - let mut pallets_tokens = TokenStream2::new(); - for pallet_declaration in pallets_except_system { - if let Some(pallet_entry) = pallet_declaration.find_part("Origin") { - let pallet = &pallet_declaration.pallet; - let instance = pallet_declaration.instance.as_ref(); - let generics = &pallet_entry.generics; - if instance.is_some() && generics.params.is_empty() { - let msg = format!( - "Instantiable pallet with no generic `Origin` cannot \ - be constructed: pallet `{}` must have generic `Origin`", - pallet_declaration.name - ); - return Err(syn::Error::new(pallet_declaration.name.span(), msg)); - } - let index = pallet_declaration.index; - let tokens = quote!(#[codec(index = #index)] #pallet #instance #generics,); - pallets_tokens.extend(tokens); - } - } - - let system_name = &system_pallet.pallet; - let system_index = system_pallet.index; - - Ok(quote!( - #scrate::impl_outer_origin! { - pub enum Origin for #runtime_name where - system = #system_name, - system_index = #system_index - { - #pallets_tokens - } - } - )) -} - -fn decl_outer_event<'a>( - runtime_name: &'a Ident, - pallet_declarations: impl Iterator<Item = &'a Pallet>, - scrate: &'a TokenStream2, -) -> syn::Result<TokenStream2> { - let mut pallets_tokens = TokenStream2::new(); - for pallet_declaration in pallet_declarations { - if let Some(pallet_entry) = pallet_declaration.find_part("Event") { - let pallet = &pallet_declaration.pallet; - let instance = pallet_declaration.instance.as_ref(); - let generics = &pallet_entry.generics; - if instance.is_some() && generics.params.is_empty() { - let msg = format!( - "Instantiable pallet with no generic `Event` cannot \ - be constructed: pallet `{}` must have generic `Event`", - pallet_declaration.name, - ); - return Err(syn::Error::new(pallet_declaration.name.span(), msg)); - } - - let index = pallet_declaration.index; - let tokens = quote!(#[codec(index = #index)] #pallet #instance #generics,); - pallets_tokens.extend(tokens); - } - } - - Ok(quote!( - #scrate::impl_outer_event! { - pub enum Event for #runtime_name { - #pallets_tokens - } - } - )) -} - fn decl_all_pallets<'a>( runtime: &'a Ident, pallet_declarations: impl Iterator<Item = &'a Pallet>, diff --git a/substrate/frame/support/procedural/src/construct_runtime/parse.rs b/substrate/frame/support/procedural/src/construct_runtime/parse.rs index def207439b5369b9b4b34083fb575c5201289a9f..390729865e98d84deed72e7ca080210ec10e7f0f 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/parse.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/parse.rs @@ -16,12 +16,14 @@ // limitations under the License. use frame_support_procedural_tools::syn_ext as ext; -use proc_macro2::Span; +use proc_macro2::{Span, TokenStream}; use std::collections::HashSet; use syn::{ + ext::IdentExt, parse::{Parse, ParseStream}, + punctuated::Punctuated, spanned::Spanned, - token, Error, Ident, Result, Token, + token, Error, Ident, Path, PathArguments, PathSegment, Result, Token, }; mod keyword { @@ -154,7 +156,7 @@ pub struct PalletDeclaration { pub name: Ident, /// Optional fixed index (e.g. `MyPallet ... = 3,`) pub index: Option<u8>, - pub pallet: Ident, + pub pallet: PalletPath, pub instance: Option<Ident>, pub pallet_parts: Vec<PalletPart>, } @@ -164,17 +166,16 @@ impl Parse for PalletDeclaration { let name = input.parse()?; let _: Token![:] = input.parse()?; let pallet = input.parse()?; - let instance = if input.peek(Token![::]) && input.peek3(Token![<]) { - let _: Token![::] = input.parse()?; + let instance = if input.peek(Token![<]) { let _: Token![<] = input.parse()?; let res = Some(input.parse()?); let _: Token![>] = input.parse()?; + let _: Token![::] = input.parse()?; res } else { None }; - let _: Token![::] = input.parse()?; let pallet_parts = parse_pallet_parts(input)?; let index = if input.peek(Token![=]) { @@ -198,6 +199,84 @@ impl Parse for PalletDeclaration { } } +/// A struct representing a path to a pallet. `PalletPath` is almost identical to the standard +/// Rust path with a few restrictions: +/// - No leading colons allowed +/// - Path segments can only consist of identifers; angle-bracketed or parenthesized segments will +/// result in a parsing error (except when specifying instances) +#[derive(Debug, Clone)] +pub struct PalletPath { + pub inner: Path, +} + +impl Parse for PalletPath { + fn parse(input: ParseStream) -> Result<Self> { + let mut lookahead = input.lookahead1(); + let mut segments = Punctuated::new(); + + if lookahead.peek(Token![crate]) + || lookahead.peek(Token![self]) + || lookahead.peek(Token![super]) + || lookahead.peek(Ident) + { + let ident = input.call(Ident::parse_any)?; + segments.push(PathSegment { ident, arguments: PathArguments::None }); + let _: Token![::] = input.parse()?; + lookahead = input.lookahead1(); + } else { + return Err(lookahead.error()); + } + + while lookahead.peek(Ident) { + let ident = input.parse()?; + segments.push(PathSegment { ident, arguments: PathArguments::None }); + let _: Token![::] = input.parse()?; + lookahead = input.lookahead1(); + } + + if !lookahead.peek(token::Brace) && !lookahead.peek(Token![<]) { + return Err(lookahead.error()); + } + + Ok(Self { + inner: Path { + leading_colon: None, + segments, + } + }) + } +} + +impl PalletPath { + /// Return the snake-cased module name for this path. + pub fn mod_name(&self) -> Ident { + let mut iter = self.inner.segments.iter(); + let mut mod_name = match &iter.next().expect("Path should always have 1 segment; qed").ident { + ident if ident == "self" || ident == "super" || ident == "crate" => { + // Skip `crate`, `self` and `super` quasi-keywords when creating the module name + iter.next() + .expect("There must be a path segment pointing to a pallet following \ + `crate`, `self` or `super`; qed") + .ident + .clone() + } + ident => ident.clone(), + }; + + for segment in iter { + mod_name = quote::format_ident!("{}_{}", mod_name, segment.ident); + } + + mod_name + } +} + +impl quote::ToTokens for PalletPath { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.inner.to_tokens(tokens); + } +} + /// Parse [`PalletPart`]'s from a braces enclosed list that is split by commas, e.g. /// /// `{ Call, Event }` @@ -271,11 +350,6 @@ impl PalletPartKeyword { } } - /// Returns the name as `Ident`. - fn ident(&self) -> Ident { - Ident::new(self.name(), self.span()) - } - /// Returns `true` if this pallet part is allowed to have generic arguments. fn allows_generic(&self) -> bool { Self::all_generic_arg().iter().any(|n| *n == self.name()) @@ -341,11 +415,6 @@ impl PalletPart { pub fn name(&self) -> &'static str { self.keyword.name() } - - /// The name of this pallet part as `Ident`. - pub fn ident(&self) -> Ident { - self.keyword.ident() - } } fn remove_kind( diff --git a/substrate/frame/support/test/tests/construct_runtime.rs b/substrate/frame/support/test/tests/construct_runtime.rs index 76e28a3b152ff6a7802e3c52899caef7d53fe080..6b0a7091edff934c8d12fc5115e6c7c3c2877ca0 100644 --- a/substrate/frame/support/test/tests/construct_runtime.rs +++ b/substrate/frame/support/test/tests/construct_runtime.rs @@ -112,8 +112,96 @@ mod module2 { } } +mod nested { + use super::*; + + pub mod module3 { + use super::*; + + pub trait Config: system::Config {} + + frame_support::decl_module! { + pub struct Module<T: Config> for enum Call + where origin: <T as system::Config>::Origin, system=system + { + #[weight = 0] + pub fn fail(_origin) -> frame_support::dispatch::DispatchResult { + Err(Error::<T>::Something.into()) + } + + fn integrity_test() { + INTEGRITY_TEST_EXEC.with(|i| *i.borrow_mut() += 1); + } + } + } + + #[derive(Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode)] + pub struct Origin; + + frame_support::decl_event! { + pub enum Event { + A, + } + } + + frame_support::decl_error! { + pub enum Error for Module<T: Config> { + Something + } + } + + frame_support::decl_storage! { + trait Store for Module<T: Config> as Module {} + add_extra_genesis { + build(|_config| {}) + } + } + } +} + +pub mod module3 { + use super::*; + + pub trait Config: system::Config {} + + frame_support::decl_module! { + pub struct Module<T: Config> for enum Call + where origin: <T as system::Config>::Origin, system=system + { + #[weight = 0] + pub fn fail(_origin) -> frame_support::dispatch::DispatchResult { + Err(Error::<T>::Something.into()) + } + } + } + + #[derive(Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode)] + pub struct Origin<T>(pub core::marker::PhantomData<T>); + + frame_support::decl_event! { + pub enum Event { + A, + } + } + + frame_support::decl_error! { + pub enum Error for Module<T: Config> { + Something + } + } + + frame_support::decl_storage! { + trait Store for Module<T: Config> as Module {} + add_extra_genesis { + build(|_config| {}) + } + } +} + impl<I> module1::Config<I> for Runtime {} impl module2::Config for Runtime {} +impl nested::module3::Config for Runtime {} +impl module3::Config for Runtime {} pub type Signature = sr25519::Signature; pub type AccountId = <Signature as Verify>::Signer; @@ -142,6 +230,8 @@ frame_support::construct_runtime!( Module1_1: module1::<Instance1>::{Pallet, Call, Storage, Event<T>, Origin<T>}, Module2: module2::{Pallet, Call, Storage, Event, Origin}, Module1_2: module1::<Instance2>::{Pallet, Call, Storage, Event<T>, Origin<T>}, + NestedModule3: nested::module3::{Pallet, Call, Config, Storage, Event, Origin}, + Module3: self::module3::{Pallet, Call, Config, Storage, Event, Origin<T>}, Module1_3: module1::<Instance3>::{Pallet, Storage} = 6, Module1_4: module1::<Instance4>::{Pallet, Call} = 3, Module1_5: module1::<Instance5>::{Pallet, Event<T>}, @@ -156,6 +246,82 @@ pub type Header = generic::Header<BlockNumber, BlakeTwo256>; pub type Block = generic::Block<Header, UncheckedExtrinsic>; pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, Call, Signature, ()>; +mod origin_test { + use frame_support::traits::{Filter, OriginTrait}; + use super::{module3, nested, system, Block, UncheckedExtrinsic}; + + impl nested::module3::Config for RuntimeOriginTest {} + impl module3::Config for RuntimeOriginTest {} + + pub struct BaseCallFilter; + impl Filter<Call> for BaseCallFilter { + fn filter(c: &Call) -> bool { + match c { + Call::NestedModule3(_) => true, + _ => false, + } + } + } + + impl system::Config for RuntimeOriginTest { + type BaseCallFilter = BaseCallFilter; + type Hash = super::H256; + type Origin = Origin; + type BlockNumber = super::BlockNumber; + type AccountId = u32; + type Event = Event; + type PalletInfo = PalletInfo; + type Call = Call; + type DbWeight = (); + } + + frame_support::construct_runtime!( + pub enum RuntimeOriginTest where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: system::{Pallet, Event<T>, Origin<T>}, + NestedModule3: nested::module3::{Pallet, Origin, Call}, + Module3: module3::{Pallet, Origin<T>, Call}, + } + ); + + #[test] + fn origin_default_filter() { + let accepted_call = nested::module3::Call::fail().into(); + let rejected_call = module3::Call::fail().into(); + + assert_eq!(Origin::root().filter_call(&accepted_call), true); + assert_eq!(Origin::root().filter_call(&rejected_call), true); + assert_eq!(Origin::none().filter_call(&accepted_call), true); + assert_eq!(Origin::none().filter_call(&rejected_call), false); + assert_eq!(Origin::signed(0).filter_call(&accepted_call), true); + assert_eq!(Origin::signed(0).filter_call(&rejected_call), false); + assert_eq!(Origin::from(Some(0)).filter_call(&accepted_call), true); + assert_eq!(Origin::from(Some(0)).filter_call(&rejected_call), false); + assert_eq!(Origin::from(None).filter_call(&accepted_call), true); + assert_eq!(Origin::from(None).filter_call(&rejected_call), false); + assert_eq!(Origin::from(super::nested::module3::Origin).filter_call(&accepted_call), true); + assert_eq!(Origin::from(super::nested::module3::Origin).filter_call(&rejected_call), false); + + let mut origin = Origin::from(Some(0)); + + origin.add_filter(|c| matches!(c, Call::Module3(_))); + assert_eq!(origin.filter_call(&accepted_call), false); + assert_eq!(origin.filter_call(&rejected_call), false); + + origin.set_caller_from(Origin::root()); + assert!(matches!(origin.caller, OriginCaller::system(super::system::RawOrigin::Root))); + assert_eq!(origin.filter_call(&accepted_call), false); + assert_eq!(origin.filter_call(&rejected_call), false); + + origin.reset_filter(); + assert_eq!(origin.filter_call(&accepted_call), true); + assert_eq!(origin.filter_call(&rejected_call), false); + } +} + #[test] fn check_modules_error_type() { assert_eq!( @@ -170,6 +336,10 @@ fn check_modules_error_type() { Module1_2::fail(system::Origin::<Runtime>::Root.into()), Err(DispatchError::Module { index: 33, error: 0, message: Some("Something") }), ); + assert_eq!( + NestedModule3::fail(system::Origin::<Runtime>::Root.into()), + Err(DispatchError::Module { index: 34, error: 0, message: Some("Something") }), + ); assert_eq!( Module1_3::fail(system::Origin::<Runtime>::Root.into()), Err(DispatchError::Module { index: 6, error: 0, message: Some("Something") }), @@ -203,7 +373,7 @@ fn check_modules_error_type() { #[test] fn integrity_test_works() { __construct_runtime_integrity_test::runtime_integrity_tests(); - assert_eq!(INTEGRITY_TEST_EXEC.with(|i| *i.borrow()), 1); + assert_eq!(INTEGRITY_TEST_EXEC.with(|i| *i.borrow()), 2); } #[test] @@ -222,6 +392,12 @@ fn origin_codec() { let origin = OriginCaller::module1_Instance2(module1::Origin(Default::default())); assert_eq!(origin.encode()[0], 33); + let origin = OriginCaller::nested_module3(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_Instance6(module1::Origin(Default::default())); assert_eq!(origin.encode()[0], 1); @@ -251,6 +427,12 @@ fn event_codec() { let event = module1::Event::<Runtime, module1::Instance2>::A(Default::default()); assert_eq!(Event::from(event).encode()[0], 33); + let event = nested::module3::Event::A; + assert_eq!(Event::from(event).encode()[0], 34); + + let event = module3::Event::A; + assert_eq!(Event::from(event).encode()[0], 35); + let event = module1::Event::<Runtime, module1::Instance5>::A(Default::default()); assert_eq!(Event::from(event).encode()[0], 4); @@ -274,6 +456,8 @@ fn call_codec() { assert_eq!(Call::Module1_1(module1::Call::fail()).encode()[0], 31); assert_eq!(Call::Module2(module2::Call::fail()).encode()[0], 32); assert_eq!(Call::Module1_2(module1::Call::fail()).encode()[0], 33); + assert_eq!(Call::NestedModule3(nested::module3::Call::fail()).encode()[0], 34); + assert_eq!(Call::Module3(module3::Call::fail()).encode()[0], 35); assert_eq!(Call::Module1_4(module1::Call::fail()).encode()[0], 3); assert_eq!(Call::Module1_6(module1::Call::fail()).encode()[0], 1); assert_eq!(Call::Module1_7(module1::Call::fail()).encode()[0], 2); @@ -381,6 +565,54 @@ fn test_metadata() { errors: DecodeDifferent::Encode(FnEncode(|| &[])), index: 33, }, + ModuleMetadata { + name: DecodeDifferent::Encode("NestedModule3"), + storage: Some(DecodeDifferent::Encode(FnEncode(|| StorageMetadata { + prefix: DecodeDifferent::Encode("Module"), + entries: DecodeDifferent::Encode(&[]), + }))), + calls: Some(DecodeDifferent::Encode(FnEncode(|| &[ + FunctionMetadata { + name: DecodeDifferent::Encode("fail"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }, + ]))), + event: Some(DecodeDifferent::Encode(FnEncode(|| &[ + EventMetadata { + name: DecodeDifferent::Encode("A"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }, + ]))), + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), + index: 34, + }, + ModuleMetadata { + name: DecodeDifferent::Encode("Module3"), + storage: Some(DecodeDifferent::Encode(FnEncode(|| StorageMetadata { + prefix: DecodeDifferent::Encode("Module"), + entries: DecodeDifferent::Encode(&[]), + }))), + calls: Some(DecodeDifferent::Encode(FnEncode(|| &[ + FunctionMetadata { + name: DecodeDifferent::Encode("fail"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }, + ]))), + event: Some(DecodeDifferent::Encode(FnEncode(|| &[ + EventMetadata { + name: DecodeDifferent::Encode("A"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }, + ]))), + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), + index: 35, + }, ModuleMetadata { name: DecodeDifferent::Encode("Module1_3"), storage: Some(DecodeDifferent::Encode(FnEncode(|| StorageMetadata { @@ -522,6 +754,12 @@ fn pallet_in_runtime_is_correct() { assert_eq!(PalletInfo::index::<Module1_2>().unwrap(), 33); assert_eq!(PalletInfo::name::<Module1_2>().unwrap(), "Module1_2"); + assert_eq!(PalletInfo::index::<NestedModule3>().unwrap(), 34); + assert_eq!(PalletInfo::name::<NestedModule3>().unwrap(), "NestedModule3"); + + assert_eq!(PalletInfo::index::<Module3>().unwrap(), 35); + assert_eq!(PalletInfo::name::<Module3>().unwrap(), "Module3"); + assert_eq!(PalletInfo::index::<Module1_3>().unwrap(), 6); assert_eq!(PalletInfo::name::<Module1_3>().unwrap(), "Module1_3"); diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/empty_pallet_path.rs b/substrate/frame/support/test/tests/construct_runtime_ui/empty_pallet_path.rs new file mode 100644 index 0000000000000000000000000000000000000000..bc6abfa82b9cd81af3a56fedc86cac886a36e800 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/empty_pallet_path.rs @@ -0,0 +1,13 @@ +use frame_support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + system: , + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/empty_pallet_path.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/empty_pallet_path.stderr new file mode 100644 index 0000000000000000000000000000000000000000..7102076e5acb05b30d28532db38df72278108b83 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/empty_pallet_path.stderr @@ -0,0 +1,5 @@ +error: expected one of: `crate`, `self`, `super`, identifier + --> $DIR/empty_pallet_path.rs:9:11 + | +9 | system: , + | ^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details.stderr index 559a4637d67ff2cb363c639c7e21765d568ae826..50505b9130cbe5071cb1236276436540ac02ddfa 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details.stderr +++ b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details.stderr @@ -1,4 +1,4 @@ -error: expected curly braces +error: expected one of: identifier, curly braces, `<` --> $DIR/invalid_module_details.rs:9:19 | 9 | system: System::(),