// This file is part of Substrate. // Copyright (C) 2020-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 frame_support::{ dispatch::UnfilteredDispatchable, storage::unhashed, traits::{GetCallName, OnFinalize, OnGenesis, OnInitialize, OnRuntimeUpgrade}, weights::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays}, }; use sp_io::{ hashing::{blake2_128, twox_128, twox_64}, TestExternalities, }; use sp_runtime::DispatchError; #[frame_support::pallet] pub mod pallet { use frame_support::{pallet_prelude::*, scale_info}; use frame_system::pallet_prelude::*; use sp_std::any::TypeId; type BalanceOf = >::Balance; #[pallet::config] pub trait Config: frame_system::Config { #[pallet::constant] type MyGetParam: Get; type Balance: Parameter + Default + scale_info::StaticTypeInfo; type Event: From> + IsType<::Event>; } #[pallet::pallet] #[pallet::generate_store(pub(crate) trait Store)] pub struct Pallet(PhantomData<(T, I)>); #[pallet::hooks] impl, I: 'static> Hooks> for Pallet { fn on_initialize(_: BlockNumberFor) -> Weight { if TypeId::of::() == TypeId::of::<()>() { Self::deposit_event(Event::Something(10)); 10 } else { Self::deposit_event(Event::Something(11)); 11 } } fn on_finalize(_: BlockNumberFor) { if TypeId::of::() == TypeId::of::<()>() { Self::deposit_event(Event::Something(20)); } else { Self::deposit_event(Event::Something(21)); } } fn on_runtime_upgrade() -> Weight { if TypeId::of::() == TypeId::of::<()>() { Self::deposit_event(Event::Something(30)); 30 } else { Self::deposit_event(Event::Something(31)); 31 } } fn integrity_test() {} } #[pallet::call] impl, I: 'static> Pallet { /// Doc comment put in metadata #[pallet::weight(Weight::from(*_foo))] pub fn foo( origin: OriginFor, #[pallet::compact] _foo: u32, ) -> DispatchResultWithPostInfo { let _ = origin; Self::deposit_event(Event::Something(3)); Ok(().into()) } /// Doc comment put in metadata #[pallet::weight(1)] #[frame_support::transactional] pub fn foo_transactional( origin: OriginFor, #[pallet::compact] _foo: u32, ) -> DispatchResultWithPostInfo { let _ = origin; Ok(().into()) } } #[pallet::error] pub enum Error { /// doc comment put into metadata InsufficientProposersBalance, } #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] pub enum Event, I: 'static = ()> { /// doc comment put in metadata Proposed(::AccountId), /// doc Spending(BalanceOf), Something(u32), } #[pallet::storage] pub type Value = StorageValue<_, u32>; #[pallet::storage] pub type Map = StorageMap<_, Blake2_128Concat, u8, u16>; #[pallet::storage] pub type Map2 = StorageMap<_, Twox64Concat, u16, u32>; #[pallet::storage] pub type DoubleMap = StorageDoubleMap<_, Blake2_128Concat, u8, Twox64Concat, u16, u32>; #[pallet::storage] pub type DoubleMap2 = StorageDoubleMap<_, Twox64Concat, u16, Blake2_128Concat, u32, u64>; #[pallet::storage] #[pallet::getter(fn nmap)] pub type NMap = StorageNMap<_, storage::Key, u32>; #[pallet::storage] #[pallet::getter(fn nmap2)] pub type NMap2 = StorageNMap<_, (storage::Key, storage::Key), u64>; #[pallet::genesis_config] #[derive(Default)] pub struct GenesisConfig { _myfield: u32, } #[pallet::genesis_build] impl, I: 'static> GenesisBuild for GenesisConfig { fn build(&self) {} } #[pallet::origin] #[derive( EqNoBound, RuntimeDebugNoBound, CloneNoBound, PartialEqNoBound, Encode, Decode, scale_info::TypeInfo, )] #[scale_info(skip_type_params(T, I))] pub struct Origin(PhantomData<(T, I)>); #[pallet::validate_unsigned] impl, I: 'static> ValidateUnsigned for Pallet { type Call = Call; fn validate_unsigned( _source: TransactionSource, _call: &Self::Call, ) -> TransactionValidity { Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) } } #[pallet::inherent] impl, I: 'static> ProvideInherent for Pallet { type Call = Call; type Error = InherentError; const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; fn create_inherent(_data: &InherentData) -> Option { unimplemented!(); } fn is_inherent(_call: &Self::Call) -> bool { unimplemented!(); } } #[derive(codec::Encode, sp_runtime::RuntimeDebug)] #[cfg_attr(feature = "std", derive(codec::Decode))] pub enum InherentError {} impl frame_support::inherent::IsFatalError for InherentError { fn is_fatal_error(&self) -> bool { unimplemented!(); } } pub const INHERENT_IDENTIFIER: frame_support::inherent::InherentIdentifier = *b"testpall"; } // Test that a instantiable pallet with a generic genesis_config is correctly handled #[frame_support::pallet] pub mod pallet2 { use frame_support::pallet_prelude::*; #[pallet::config] pub trait Config: frame_system::Config { type Event: From> + IsType<::Event>; } #[pallet::pallet] #[pallet::generate_store(pub(crate) trait Store)] pub struct Pallet(PhantomData<(T, I)>); #[pallet::event] pub enum Event, I: 'static = ()> { /// Something Something(u32), } #[pallet::genesis_config] pub struct GenesisConfig, I: 'static = ()> { phantom: PhantomData<(T, I)>, } impl, I: 'static> Default for GenesisConfig { fn default() -> Self { GenesisConfig { phantom: Default::default() } } } #[pallet::genesis_build] impl, I: 'static> GenesisBuild for GenesisConfig { fn build(&self) {} } } frame_support::parameter_types!( pub const MyGetParam: u32 = 10; pub const BlockHashCount: u32 = 250; ); impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type Origin = Origin; type Index = u64; type BlockNumber = u32; type Call = Call; type Hash = sp_runtime::testing::H256; type Hashing = sp_runtime::traits::BlakeTwo256; type AccountId = u64; type Lookup = sp_runtime::traits::IdentityLookup; type Header = Header; type Event = Event; type BlockHashCount = BlockHashCount; type BlockWeights = (); type BlockLength = (); type DbWeight = (); type Version = (); type PalletInfo = PalletInfo; type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); type SS58Prefix = (); type OnSetCode = (); } impl pallet::Config for Runtime { type Event = Event; type MyGetParam = MyGetParam; type Balance = u64; } impl pallet::Config for Runtime { type Event = Event; type MyGetParam = MyGetParam; type Balance = u64; } impl pallet2::Config for Runtime { type Event = Event; } impl pallet2::Config for Runtime { type Event = Event; } pub type Header = sp_runtime::generic::Header; pub type Block = sp_runtime::generic::Block; pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; frame_support::construct_runtime!( pub enum Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { // Exclude part `Storage` in order not to check its metadata in tests. System: frame_system exclude_parts { Storage }, Example: pallet, Instance1Example: pallet::, Example2: pallet2, Instance1Example2: pallet2::, } ); #[test] fn call_expand() { let call_foo = pallet::Call::::foo { foo: 3 }; assert_eq!( call_foo.get_dispatch_info(), DispatchInfo { weight: 3, class: DispatchClass::Normal, pays_fee: Pays::Yes } ); assert_eq!(call_foo.get_call_name(), "foo"); assert_eq!(pallet::Call::::get_call_names(), &["foo", "foo_transactional"]); let call_foo = pallet::Call::::foo { foo: 3 }; assert_eq!( call_foo.get_dispatch_info(), DispatchInfo { weight: 3, class: DispatchClass::Normal, pays_fee: Pays::Yes } ); assert_eq!(call_foo.get_call_name(), "foo"); assert_eq!( pallet::Call::::get_call_names(), &["foo", "foo_transactional"], ); } #[test] fn error_expand() { assert_eq!( format!("{:?}", pallet::Error::::InsufficientProposersBalance), String::from("InsufficientProposersBalance"), ); assert_eq!( <&'static str>::from(pallet::Error::::InsufficientProposersBalance), "InsufficientProposersBalance", ); assert_eq!( DispatchError::from(pallet::Error::::InsufficientProposersBalance), DispatchError::Module { index: 1, error: 0, message: Some("InsufficientProposersBalance") }, ); assert_eq!( format!("{:?}", pallet::Error::::InsufficientProposersBalance), String::from("InsufficientProposersBalance"), ); assert_eq!( <&'static str>::from( pallet::Error::::InsufficientProposersBalance ), "InsufficientProposersBalance", ); assert_eq!( DispatchError::from( pallet::Error::::InsufficientProposersBalance ), DispatchError::Module { index: 2, error: 0, message: Some("InsufficientProposersBalance") }, ); } #[test] fn instance_expand() { // assert same type let _: pallet::__InherentHiddenInstance = (); } #[test] fn pallet_expand_deposit_event() { TestExternalities::default().execute_with(|| { frame_system::Pallet::::set_block_number(1); pallet::Call::::foo { foo: 3 } .dispatch_bypass_filter(None.into()) .unwrap(); assert_eq!( frame_system::Pallet::::events()[0].event, Event::Example(pallet::Event::Something(3)), ); }); TestExternalities::default().execute_with(|| { frame_system::Pallet::::set_block_number(1); pallet::Call::::foo { foo: 3 } .dispatch_bypass_filter(None.into()) .unwrap(); assert_eq!( frame_system::Pallet::::events()[0].event, Event::Instance1Example(pallet::Event::Something(3)), ); }); } #[test] fn storage_expand() { use frame_support::{pallet_prelude::*, storage::StoragePrefixedMap}; fn twox_64_concat(d: &[u8]) -> Vec { let mut v = twox_64(d).to_vec(); v.extend_from_slice(d); v } fn blake2_128_concat(d: &[u8]) -> Vec { let mut v = blake2_128(d).to_vec(); v.extend_from_slice(d); v } TestExternalities::default().execute_with(|| { >::put(1); let k = [twox_128(b"Example"), twox_128(b"Value")].concat(); assert_eq!(unhashed::get::(&k), Some(1u32)); >::insert(1, 2); let mut k = [twox_128(b"Example"), twox_128(b"Map")].concat(); k.extend(1u8.using_encoded(blake2_128_concat)); assert_eq!(unhashed::get::(&k), Some(2u16)); assert_eq!(&k[..32], &>::final_prefix()); >::insert(1, 2); let mut k = [twox_128(b"Example"), twox_128(b"Map2")].concat(); k.extend(1u16.using_encoded(twox_64_concat)); assert_eq!(unhashed::get::(&k), Some(2u32)); assert_eq!(&k[..32], &>::final_prefix()); >::insert(&1, &2, &3); let mut k = [twox_128(b"Example"), twox_128(b"DoubleMap")].concat(); k.extend(1u8.using_encoded(blake2_128_concat)); k.extend(2u16.using_encoded(twox_64_concat)); assert_eq!(unhashed::get::(&k), Some(3u32)); assert_eq!(&k[..32], &>::final_prefix()); >::insert(&1, &2, &3); let mut k = [twox_128(b"Example"), twox_128(b"DoubleMap2")].concat(); k.extend(1u16.using_encoded(twox_64_concat)); k.extend(2u32.using_encoded(blake2_128_concat)); assert_eq!(unhashed::get::(&k), Some(3u64)); assert_eq!(&k[..32], &>::final_prefix()); >::insert((&1,), &3); let mut k = [twox_128(b"Example"), twox_128(b"NMap")].concat(); k.extend(1u8.using_encoded(blake2_128_concat)); assert_eq!(unhashed::get::(&k), Some(3u32)); assert_eq!(&k[..32], &>::final_prefix()); >::insert((&1, &2), &3); let mut k = [twox_128(b"Example"), twox_128(b"NMap2")].concat(); k.extend(1u16.using_encoded(twox_64_concat)); k.extend(2u32.using_encoded(blake2_128_concat)); assert_eq!(unhashed::get::(&k), Some(3u64)); assert_eq!(&k[..32], &>::final_prefix()); }); TestExternalities::default().execute_with(|| { >::put(1); let k = [twox_128(b"Instance1Example"), twox_128(b"Value")].concat(); assert_eq!(unhashed::get::(&k), Some(1u32)); >::insert(1, 2); let mut k = [twox_128(b"Instance1Example"), twox_128(b"Map")].concat(); k.extend(1u8.using_encoded(blake2_128_concat)); assert_eq!(unhashed::get::(&k), Some(2u16)); assert_eq!(&k[..32], &>::final_prefix()); >::insert(1, 2); let mut k = [twox_128(b"Instance1Example"), twox_128(b"Map2")].concat(); k.extend(1u16.using_encoded(twox_64_concat)); assert_eq!(unhashed::get::(&k), Some(2u32)); assert_eq!(&k[..32], &>::final_prefix()); >::insert(&1, &2, &3); let mut k = [twox_128(b"Instance1Example"), twox_128(b"DoubleMap")].concat(); k.extend(1u8.using_encoded(blake2_128_concat)); k.extend(2u16.using_encoded(twox_64_concat)); assert_eq!(unhashed::get::(&k), Some(3u32)); assert_eq!(&k[..32], &>::final_prefix()); >::insert(&1, &2, &3); let mut k = [twox_128(b"Instance1Example"), twox_128(b"DoubleMap2")].concat(); k.extend(1u16.using_encoded(twox_64_concat)); k.extend(2u32.using_encoded(blake2_128_concat)); assert_eq!(unhashed::get::(&k), Some(3u64)); assert_eq!(&k[..32], &>::final_prefix()); >::insert((&1,), &3); let mut k = [twox_128(b"Instance1Example"), twox_128(b"NMap")].concat(); k.extend(1u8.using_encoded(blake2_128_concat)); assert_eq!(unhashed::get::(&k), Some(3u32)); assert_eq!(&k[..32], &>::final_prefix()); >::insert((&1, &2), &3); let mut k = [twox_128(b"Instance1Example"), twox_128(b"NMap2")].concat(); k.extend(1u16.using_encoded(twox_64_concat)); k.extend(2u32.using_encoded(blake2_128_concat)); assert_eq!(unhashed::get::(&k), Some(3u64)); assert_eq!(&k[..32], &>::final_prefix()); }); } #[test] fn pallet_metadata_expands() { use frame_support::traits::{CrateVersion, PalletInfoData, PalletsInfoAccess}; let mut infos = AllPalletsWithSystem::infos(); infos.sort_by_key(|x| x.index); assert_eq!( infos, vec![ PalletInfoData { index: 0, name: "System", module_name: "frame_system", crate_version: CrateVersion { major: 4, minor: 0, patch: 0 }, }, PalletInfoData { index: 1, name: "Example", module_name: "pallet", crate_version: CrateVersion { major: 3, minor: 0, patch: 0 }, }, PalletInfoData { index: 2, name: "Instance1Example", module_name: "pallet", crate_version: CrateVersion { major: 3, minor: 0, patch: 0 }, }, PalletInfoData { index: 3, name: "Example2", module_name: "pallet2", crate_version: CrateVersion { major: 3, minor: 0, patch: 0 }, }, PalletInfoData { index: 4, name: "Instance1Example2", module_name: "pallet2", crate_version: CrateVersion { major: 3, minor: 0, patch: 0 }, }, ] ); } #[test] fn pallet_hooks_expand() { TestExternalities::default().execute_with(|| { frame_system::Pallet::::set_block_number(1); assert_eq!(AllPallets::on_initialize(1), 21); AllPallets::on_finalize(1); assert_eq!(AllPallets::on_runtime_upgrade(), 61); // The order is indeed reversed due to https://github.com/paritytech/substrate/issues/6280 assert_eq!( frame_system::Pallet::::events()[0].event, Event::Instance1Example(pallet::Event::Something(11)), ); assert_eq!( frame_system::Pallet::::events()[1].event, Event::Example(pallet::Event::Something(10)), ); assert_eq!( frame_system::Pallet::::events()[2].event, Event::Instance1Example(pallet::Event::Something(21)), ); assert_eq!( frame_system::Pallet::::events()[3].event, Event::Example(pallet::Event::Something(20)), ); assert_eq!( frame_system::Pallet::::events()[4].event, Event::Instance1Example(pallet::Event::Something(31)), ); assert_eq!( frame_system::Pallet::::events()[5].event, Event::Example(pallet::Event::Something(30)), ); }) } #[test] fn pallet_on_genesis() { TestExternalities::default().execute_with(|| { pallet::Pallet::::on_genesis(); pallet::Pallet::::on_genesis(); }) } #[test] fn metadata() { use frame_support::metadata::*; let system_pallet_metadata = PalletMetadata { index: 0, name: "System", storage: None, // The storage metadatas have been excluded. calls: Some(scale_info::meta_type::>().into()), event: Some(PalletEventMetadata { ty: scale_info::meta_type::>(), }), constants: vec![ PalletConstantMetadata { name: "BlockWeights", ty: scale_info::meta_type::(), value: vec![], docs: vec![], }, PalletConstantMetadata { name: "BlockLength", ty: scale_info::meta_type::(), value: vec![], docs: vec![], }, PalletConstantMetadata { name: "BlockHashCount", ty: scale_info::meta_type::(), value: vec![], docs: vec![], }, PalletConstantMetadata { name: "DbWeight", ty: scale_info::meta_type::(), value: vec![], docs: vec![], }, PalletConstantMetadata { name: "Version", ty: scale_info::meta_type::(), value: vec![], docs: vec![], }, PalletConstantMetadata { name: "SS58Prefix", ty: scale_info::meta_type::(), value: vec![], docs: vec![], }, ], error: Some(PalletErrorMetadata { ty: scale_info::meta_type::>(), }), }; let example_pallet_metadata = PalletMetadata { index: 1, name: "Example", storage: Some(PalletStorageMetadata { prefix: "Example", entries: vec![ StorageEntryMetadata { name: "Value", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Plain(scale_info::meta_type::()), default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "Map", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { key: scale_info::meta_type::(), value: scale_info::meta_type::(), hashers: vec![StorageHasher::Blake2_128Concat], }, default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "Map2", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { key: scale_info::meta_type::(), value: scale_info::meta_type::(), hashers: vec![StorageHasher::Twox64Concat], }, default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "DoubleMap", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { value: scale_info::meta_type::(), key: scale_info::meta_type::<(u8, u16)>(), hashers: vec![StorageHasher::Blake2_128Concat, StorageHasher::Twox64Concat], }, default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "DoubleMap2", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { value: scale_info::meta_type::(), key: scale_info::meta_type::<(u16, u32)>(), hashers: vec![StorageHasher::Twox64Concat, StorageHasher::Blake2_128Concat], }, default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "NMap", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { key: scale_info::meta_type::(), hashers: vec![StorageHasher::Blake2_128Concat], value: scale_info::meta_type::(), }, default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "NMap2", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { key: scale_info::meta_type::<(u16, u32)>(), hashers: vec![StorageHasher::Twox64Concat, StorageHasher::Blake2_128Concat], value: scale_info::meta_type::(), }, default: vec![0], docs: vec![], }, ], }), calls: Some(scale_info::meta_type::>().into()), event: Some(PalletEventMetadata { ty: scale_info::meta_type::>() }), constants: vec![PalletConstantMetadata { name: "MyGetParam", ty: scale_info::meta_type::(), value: vec![10, 0, 0, 0], docs: vec![], }], error: Some(PalletErrorMetadata { ty: scale_info::meta_type::>() }), }; let mut example_pallet_instance1_metadata = example_pallet_metadata.clone(); example_pallet_instance1_metadata.name = "Instance1Example"; example_pallet_instance1_metadata.index = 2; match example_pallet_instance1_metadata.calls { Some(ref mut calls_meta) => { calls_meta.ty = scale_info::meta_type::>(); }, _ => unreachable!(), } match example_pallet_instance1_metadata.event { Some(ref mut event_meta) => { event_meta.ty = scale_info::meta_type::>(); }, _ => unreachable!(), } match example_pallet_instance1_metadata.error { Some(ref mut error_meta) => { error_meta.ty = scale_info::meta_type::>(); }, _ => unreachable!(), } match example_pallet_instance1_metadata.storage { Some(ref mut storage_meta) => { storage_meta.prefix = "Instance1Example"; }, _ => unreachable!(), } let pallets = vec![system_pallet_metadata, example_pallet_metadata, example_pallet_instance1_metadata]; let extrinsic = ExtrinsicMetadata { ty: scale_info::meta_type::(), version: 4, signed_extensions: vec![SignedExtensionMetadata { identifier: "UnitSignedExtension", ty: scale_info::meta_type::<()>(), additional_signed: scale_info::meta_type::<()>(), }], }; let expected_metadata: RuntimeMetadataPrefixed = RuntimeMetadataLastVersion::new(pallets, extrinsic, scale_info::meta_type::()) .into(); let expected_metadata = match expected_metadata.1 { RuntimeMetadata::V14(metadata) => metadata, _ => panic!("metadata has been bumped, test needs to be updated"), }; let actual_metadata = match Runtime::metadata().1 { RuntimeMetadata::V14(metadata) => metadata, _ => panic!("metadata has been bumped, test needs to be updated"), }; pretty_assertions::assert_eq!(actual_metadata.pallets[1], expected_metadata.pallets[1]); pretty_assertions::assert_eq!(actual_metadata.pallets[2], expected_metadata.pallets[2]); } #[test] fn test_pallet_info_access() { assert_eq!(::name(), "System"); assert_eq!(::name(), "Example"); assert_eq!( ::name(), "Instance1Example" ); assert_eq!(::name(), "Example2"); assert_eq!( ::name(), "Instance1Example2" ); assert_eq!(::index(), 0); assert_eq!(::index(), 1); assert_eq!(::index(), 2); assert_eq!(::index(), 3); assert_eq!(::index(), 4); }