// This file is part of Substrate. // Copyright (C) 2019-2022 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 declareed with decl_error works //! * integrity test is generated #![recursion_limit = "128"] use codec::MaxEncodedLen; use frame_support::{parameter_types, traits::PalletInfo as _}; use scale_info::TypeInfo; use sp_core::{sr25519, H256}; use sp_runtime::{ generic, traits::{BlakeTwo256, Verify}, DispatchError, ModuleError, }; mod system; pub trait Currency {} parameter_types! { pub static IntegrityTestExec: u32 = 0; } mod module1 { use super::*; pub trait Config: system::Config {} frame_support::decl_module! { pub struct Module, I: Instance = DefaultInstance> for enum Call where origin: ::RuntimeOrigin, system=system { #[weight = 0] pub fn fail(_origin) -> frame_support::dispatch::DispatchResult { Err(Error::::Something.into()) } } } #[derive( Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode, TypeInfo, MaxEncodedLen, )] pub struct Origin(pub core::marker::PhantomData<(T, I)>); frame_support::decl_event! { pub enum Event where ::AccountId { A(AccountId), } } frame_support::decl_error! { pub enum Error for Module, I: Instance> { Something } } frame_support::decl_storage! { trait Store for Module, I: Instance=DefaultInstance> as Module {} } } mod module2 { use super::*; pub trait Config: system::Config {} frame_support::decl_module! { pub struct Module for enum Call where origin: ::RuntimeOrigin, system=system { #[weight = 0] pub fn fail(_origin) -> frame_support::dispatch::DispatchResult { Err(Error::::Something.into()) } fn integrity_test() { IntegrityTestExec::mutate(|i| *i += 1); } } } #[derive( Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode, TypeInfo, MaxEncodedLen, )] pub struct Origin; frame_support::decl_event! { pub enum Event { A, } } frame_support::decl_error! { pub enum Error for Module { Something } } frame_support::decl_storage! { trait Store for Module as Module {} } } mod nested { use super::*; pub mod module3 { use super::*; pub trait Config: system::Config {} frame_support::decl_module! { pub struct Module for enum Call where origin: ::RuntimeOrigin, system=system { #[weight = 0] pub fn fail(_origin) -> frame_support::dispatch::DispatchResult { Err(Error::::Something.into()) } fn integrity_test() { IntegrityTestExec::mutate(|i| *i += 1); } } } #[derive( Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode, TypeInfo, MaxEncodedLen, )] pub struct Origin; frame_support::decl_event! { pub enum Event { A, } } frame_support::decl_error! { pub enum Error for Module { Something } } frame_support::decl_storage! { trait Store for Module as Module {} add_extra_genesis { build(|_config| {}) } } } } pub mod module3 { use super::*; pub trait Config: system::Config {} frame_support::decl_module! { pub struct Module for enum Call where origin: ::RuntimeOrigin, system=system { #[weight = 0] pub fn fail(_origin) -> frame_support::dispatch::DispatchResult { Err(Error::::Something.into()) } #[weight = 0] pub fn aux_1(_origin, #[compact] _data: u32) -> frame_support::dispatch::DispatchResult { unreachable!() } #[weight = 0] pub fn aux_2(_origin, _data: i32, #[compact] _data2: u32) -> frame_support::dispatch::DispatchResult { unreachable!() } #[weight = 0] fn aux_3(_origin, _data: i32, _data2: String) -> frame_support::dispatch::DispatchResult { unreachable!() } #[weight = 3] fn aux_4(_origin) -> frame_support::dispatch::DispatchResult { unreachable!() } #[weight = (5, frame_support::dispatch::DispatchClass::Operational)] fn operational(_origin) { unreachable!() } } } #[derive( Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode, TypeInfo, MaxEncodedLen, )] pub struct Origin(pub core::marker::PhantomData); frame_support::decl_event! { pub enum Event { A, } } frame_support::decl_error! { pub enum Error for Module { Something } } frame_support::decl_storage! { trait Store for Module as Module {} add_extra_genesis { build(|_config| {}) } } } impl module1::Config 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 = ::Signer; pub type BlockNumber = u64; pub type Index = u64; fn test_pub() -> AccountId { AccountId::from_raw([0; 32]) } impl system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type Hash = H256; type RuntimeOrigin = RuntimeOrigin; type BlockNumber = BlockNumber; type AccountId = AccountId; type RuntimeEvent = RuntimeEvent; type PalletInfo = PalletInfo; type RuntimeCall = RuntimeCall; type DbWeight = (); } frame_support::construct_runtime!( pub enum Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { System: system::{Pallet, Call, Event, Origin} = 30, Module1_1: module1::::{Pallet, Call, Storage, Event, Origin}, Module2: module2::{Pallet, Call, Storage, Event, Origin}, Module1_2: module1::::{Pallet, Call, Storage, Event, Origin}, NestedModule3: nested::module3::{Pallet, Call, Config, Storage, Event, Origin}, Module3: self::module3::{Pallet, Call, Config, Storage, Event, Origin}, Module1_3: module1::::{Pallet, Storage} = 6, Module1_4: module1::::{Pallet, Call} = 3, Module1_5: module1::::{Pallet, Event}, Module1_6: module1::::{Pallet, Call, Storage, Event, Origin} = 1, Module1_7: module1::::{Pallet, Call, Storage, Event, Origin}, Module1_8: module1::::{Pallet, Call, Storage, Event, Origin} = 12, Module1_9: module1::::{Pallet, Call, Storage, Event, Origin}, } ); pub type Header = generic::Header; pub type Block = generic::Block; pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; #[test] fn check_modules_error_type() { sp_io::TestExternalities::default().execute_with(|| { assert_eq!( Module1_1::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 31, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module2::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 32, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_2::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 33, error: [0; 4], message: Some("Something") })), ); assert_eq!( NestedModule3::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 34, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_3::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 6, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_4::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 3, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_5::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 4, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_6::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 1, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_7::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 2, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_8::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 12, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_9::fail(system::Origin::::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(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 = system::Event::::ExtrinsicSuccess; assert_eq!(RuntimeEvent::from(event).encode()[0], 30); let event = module1::Event::::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::::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::::A(test_pub()); assert_eq!(RuntimeEvent::from(event).encode()[0], 4); let event = module1::Event::::A(test_pub()); assert_eq!(RuntimeEvent::from(event).encode()[0], 1); let event = module1::Event::::A(test_pub()); assert_eq!(RuntimeEvent::from(event).encode()[0], 2); let event = module1::Event::::A(test_pub()); assert_eq!(RuntimeEvent::from(event).encode()[0], 12); let event = module1::Event::::A(test_pub()); assert_eq!(RuntimeEvent::from(event).encode()[0], 13); } #[test] fn call_codec() { use codec::Encode; assert_eq!(RuntimeCall::System(system::Call::noop {}).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 = 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 = 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 = module3::Call::fail {}; let encoded = call.encode(); assert_eq!(vec![0], encoded); let decoded = module3::Call::::decode(&mut &encoded[..]).unwrap(); assert_eq!(decoded, call); let call: module3::Call = 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::::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::::operational {}.get_dispatch_info(), DispatchInfo { weight: Weight::from_ref_time(5), class: DispatchClass::Operational, pays_fee: Pays::Yes }, ); // custom basic assert_eq!( module3::Call::::aux_4 {}.get_dispatch_info(), DispatchInfo { weight: Weight::from_ref_time(3), class: DispatchClass::Normal, pays_fee: Pays::Yes }, ); } #[test] fn call_name() { use frame_support::dispatch::GetCallName; let name = module3::Call::::aux_4 {}.get_call_name(); assert_eq!("aux_4", name); } #[test] fn call_metadata() { use frame_support::dispatch::{CallMetadata, GetCallMetadata}; let call = RuntimeCall::Module3(module3::Call::::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::dispatch::GetCallName; let call_names = module3::Call::::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::dispatch::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::::fail {}); let subcall: Option<&CallableCallFor> = call.is_sub_type(); let subcall_none: Option<&CallableCallFor> = call.is_sub_type(); assert_eq!(Some(&module3::Call::::fail {}), subcall); assert_eq!(None, subcall_none); let from = RuntimeCall::from(subcall.unwrap().clone()); assert_eq!(from, call); } #[test] fn test_metadata() { use frame_support::metadata::*; use scale_info::meta_type; let pallets = vec![ PalletMetadata { name: "System", storage: None, calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: None, index: 30, }, PalletMetadata { name: "Module1_1", storage: Some(PalletStorageMetadata { prefix: "Instance1Module", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: None, index: 31, }, PalletMetadata { name: "Module2", storage: Some(PalletStorageMetadata { prefix: "Module", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::().into()), constants: vec![], error: None, index: 32, }, PalletMetadata { name: "Module1_2", storage: Some(PalletStorageMetadata { prefix: "Instance2Module", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: None, index: 33, }, PalletMetadata { name: "NestedModule3", storage: Some(PalletStorageMetadata { prefix: "Module", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::().into()), constants: vec![], error: None, index: 34, }, PalletMetadata { name: "Module3", storage: Some(PalletStorageMetadata { prefix: "Module", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::().into()), constants: vec![], error: None, index: 35, }, PalletMetadata { name: "Module1_3", storage: Some(PalletStorageMetadata { prefix: "Instance3Module", entries: vec![] }), calls: None, event: None, constants: vec![], error: None, index: 6, }, PalletMetadata { name: "Module1_4", storage: None, calls: Some(meta_type::>().into()), event: None, constants: vec![], error: None, index: 3, }, PalletMetadata { name: "Module1_5", storage: None, calls: None, event: Some(meta_type::>().into()), constants: vec![], error: None, index: 4, }, PalletMetadata { name: "Module1_6", storage: Some(PalletStorageMetadata { prefix: "Instance6Module", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: None, index: 1, }, PalletMetadata { name: "Module1_7", storage: Some(PalletStorageMetadata { prefix: "Instance7Module", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(PalletEventMetadata { ty: meta_type::>(), }), constants: vec![], error: None, index: 2, }, PalletMetadata { name: "Module1_8", storage: Some(PalletStorageMetadata { prefix: "Instance8Module", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: None, index: 12, }, PalletMetadata { name: "Module1_9", storage: Some(PalletStorageMetadata { prefix: "Instance9Module", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: None, index: 13, }, ]; let extrinsic = ExtrinsicMetadata { ty: meta_type::(), version: 4, signed_extensions: vec![SignedExtensionMetadata { identifier: "UnitSignedExtension", ty: meta_type::<()>(), additional_signed: meta_type::<()>(), }], }; let expected_metadata: RuntimeMetadataPrefixed = RuntimeMetadataLastVersion::new(pallets, extrinsic, meta_type::()).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::().unwrap(), 30); assert_eq!(PalletInfo::name::().unwrap(), "System"); assert_eq!(PalletInfo::module_name::().unwrap(), "system"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 31); assert_eq!(PalletInfo::name::().unwrap(), "Module1_1"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 32); assert_eq!(PalletInfo::name::().unwrap(), "Module2"); assert_eq!(PalletInfo::module_name::().unwrap(), "module2"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 33); assert_eq!(PalletInfo::name::().unwrap(), "Module1_2"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 34); assert_eq!(PalletInfo::name::().unwrap(), "NestedModule3"); assert_eq!(PalletInfo::module_name::().unwrap(), "nested::module3"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 35); assert_eq!(PalletInfo::name::().unwrap(), "Module3"); assert_eq!(PalletInfo::module_name::().unwrap(), "self::module3"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 6); assert_eq!(PalletInfo::name::().unwrap(), "Module1_3"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 3); assert_eq!(PalletInfo::name::().unwrap(), "Module1_4"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 4); assert_eq!(PalletInfo::name::().unwrap(), "Module1_5"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 1); assert_eq!(PalletInfo::name::().unwrap(), "Module1_6"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 2); assert_eq!(PalletInfo::name::().unwrap(), "Module1_7"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 12); assert_eq!(PalletInfo::name::().unwrap(), "Module1_8"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 13); assert_eq!(PalletInfo::name::().unwrap(), "Module1_9"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); }