// 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::*; 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; 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::metadata(BalanceOf = "Balance", u32 = "Other")] #[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)] 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 { System: frame_system::{Pallet, Call, Event}, Example: pallet::{Pallet, Call, Event, Config, Storage, Inherent, Origin, ValidateUnsigned}, Instance1Example: pallet::::{ Pallet, Call, Event, Config, Storage, Inherent, Origin, ValidateUnsigned }, Example2: pallet2::{Pallet, Event, Config, Storage}, Instance1Example2: pallet2::::{Pallet, Event, Config, Storage}, } ); #[test] fn call_expand() { let call_foo = pallet::Call::::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(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(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(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_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 codec::{Decode, Encode}; use frame_metadata::*; let expected_pallet_metadata = ModuleMetadata { index: 1, name: DecodeDifferent::Decoded("Example".to_string()), storage: Some(DecodeDifferent::Decoded(StorageMetadata { prefix: DecodeDifferent::Decoded("Example".to_string()), entries: DecodeDifferent::Decoded(vec![ StorageEntryMetadata { name: DecodeDifferent::Decoded("Value".to_string()), modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Plain(DecodeDifferent::Decoded("u32".to_string())), default: DecodeDifferent::Decoded(vec![0]), documentation: DecodeDifferent::Decoded(vec![]), }, StorageEntryMetadata { name: DecodeDifferent::Decoded("Map".to_string()), modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { key: DecodeDifferent::Decoded("u8".to_string()), value: DecodeDifferent::Decoded("u16".to_string()), hasher: StorageHasher::Blake2_128Concat, unused: false, }, default: DecodeDifferent::Decoded(vec![0]), documentation: DecodeDifferent::Decoded(vec![]), }, StorageEntryMetadata { name: DecodeDifferent::Decoded("Map2".to_string()), modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { key: DecodeDifferent::Decoded("u16".to_string()), value: DecodeDifferent::Decoded("u32".to_string()), hasher: StorageHasher::Twox64Concat, unused: false, }, default: DecodeDifferent::Decoded(vec![0]), documentation: DecodeDifferent::Decoded(vec![]), }, StorageEntryMetadata { name: DecodeDifferent::Decoded("DoubleMap".to_string()), modifier: StorageEntryModifier::Optional, ty: StorageEntryType::DoubleMap { value: DecodeDifferent::Decoded("u32".to_string()), key1: DecodeDifferent::Decoded("u8".to_string()), key2: DecodeDifferent::Decoded("u16".to_string()), hasher: StorageHasher::Blake2_128Concat, key2_hasher: StorageHasher::Twox64Concat, }, default: DecodeDifferent::Decoded(vec![0]), documentation: DecodeDifferent::Decoded(vec![]), }, StorageEntryMetadata { name: DecodeDifferent::Decoded("DoubleMap2".to_string()), modifier: StorageEntryModifier::Optional, ty: StorageEntryType::DoubleMap { value: DecodeDifferent::Decoded("u64".to_string()), key1: DecodeDifferent::Decoded("u16".to_string()), key2: DecodeDifferent::Decoded("u32".to_string()), hasher: StorageHasher::Twox64Concat, key2_hasher: StorageHasher::Blake2_128Concat, }, default: DecodeDifferent::Decoded(vec![0]), documentation: DecodeDifferent::Decoded(vec![]), }, StorageEntryMetadata { name: DecodeDifferent::Decoded("NMap".to_string()), modifier: StorageEntryModifier::Optional, ty: StorageEntryType::NMap { keys: DecodeDifferent::Decoded(vec!["u8".to_string()]), hashers: DecodeDifferent::Decoded(vec![StorageHasher::Blake2_128Concat]), value: DecodeDifferent::Decoded("u32".to_string()), }, default: DecodeDifferent::Decoded(vec![0]), documentation: DecodeDifferent::Decoded(vec![]), }, StorageEntryMetadata { name: DecodeDifferent::Decoded("NMap2".to_string()), modifier: StorageEntryModifier::Optional, ty: StorageEntryType::NMap { keys: DecodeDifferent::Decoded(vec!["u16".to_string(), "u32".to_string()]), hashers: DecodeDifferent::Decoded(vec![ StorageHasher::Twox64Concat, StorageHasher::Blake2_128Concat, ]), value: DecodeDifferent::Decoded("u64".to_string()), }, default: DecodeDifferent::Decoded(vec![0]), documentation: DecodeDifferent::Decoded(vec![]), }, ]), })), calls: Some(DecodeDifferent::Decoded(vec![ FunctionMetadata { name: DecodeDifferent::Decoded("foo".to_string()), arguments: DecodeDifferent::Decoded(vec![FunctionArgumentMetadata { name: DecodeDifferent::Decoded("_foo".to_string()), ty: DecodeDifferent::Decoded("Compact".to_string()), }]), documentation: DecodeDifferent::Decoded(vec![ " Doc comment put in metadata".to_string() ]), }, FunctionMetadata { name: DecodeDifferent::Decoded("foo_transactional".to_string()), arguments: DecodeDifferent::Decoded(vec![FunctionArgumentMetadata { name: DecodeDifferent::Decoded("_foo".to_string()), ty: DecodeDifferent::Decoded("Compact".to_string()), }]), documentation: DecodeDifferent::Decoded(vec![ " Doc comment put in metadata".to_string() ]), }, ])), event: Some(DecodeDifferent::Decoded(vec![ EventMetadata { name: DecodeDifferent::Decoded("Proposed".to_string()), arguments: DecodeDifferent::Decoded(vec![ "::AccountId".to_string() ]), documentation: DecodeDifferent::Decoded(vec![ " doc comment put in metadata".to_string() ]), }, EventMetadata { name: DecodeDifferent::Decoded("Spending".to_string()), arguments: DecodeDifferent::Decoded(vec!["Balance".to_string()]), documentation: DecodeDifferent::Decoded(vec![" doc".to_string()]), }, EventMetadata { name: DecodeDifferent::Decoded("Something".to_string()), arguments: DecodeDifferent::Decoded(vec!["Other".to_string()]), documentation: DecodeDifferent::Decoded(vec![]), }, ])), constants: DecodeDifferent::Decoded(vec![ModuleConstantMetadata { name: DecodeDifferent::Decoded("MyGetParam".to_string()), ty: DecodeDifferent::Decoded("u32".to_string()), value: DecodeDifferent::Decoded(vec![10, 0, 0, 0]), documentation: DecodeDifferent::Decoded(vec![]), }]), errors: DecodeDifferent::Decoded(vec![ErrorMetadata { name: DecodeDifferent::Decoded("InsufficientProposersBalance".to_string()), documentation: DecodeDifferent::Decoded(vec![ " doc comment put into metadata".to_string() ]), }]), }; let mut expected_pallet_instance1_metadata = expected_pallet_metadata.clone(); expected_pallet_instance1_metadata.name = DecodeDifferent::Decoded("Instance1Example".to_string()); expected_pallet_instance1_metadata.index = 2; match expected_pallet_instance1_metadata.storage { Some(DecodeDifferent::Decoded(ref mut storage_meta)) => { storage_meta.prefix = DecodeDifferent::Decoded("Instance1Example".to_string()); }, _ => unreachable!(), } let metadata = match Runtime::metadata().1 { RuntimeMetadata::V13(metadata) => metadata, _ => panic!("metadata has been bump, test needs to be updated"), }; let modules_metadata = match metadata.modules { DecodeDifferent::Encode(modules_metadata) => modules_metadata, _ => unreachable!(), }; let pallet_metadata = ModuleMetadata::decode(&mut &modules_metadata[1].encode()[..]).unwrap(); let pallet_instance1_metadata = ModuleMetadata::decode(&mut &modules_metadata[2].encode()[..]).unwrap(); pretty_assertions::assert_eq!(pallet_metadata, expected_pallet_metadata); pretty_assertions::assert_eq!(pallet_instance1_metadata, expected_pallet_instance1_metadata); } #[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); }