// This file is part of Substrate. // Copyright (C) 2020 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::{ weights::{DispatchInfo, DispatchClass, Pays, GetDispatchInfo}, traits::{ GetCallName, GetPalletVersion, OnInitialize, OnFinalize, OnRuntimeUpgrade, OnGenesis, }, dispatch::UnfilteredDispatchable, storage::unhashed, }; use sp_runtime::{traits::Block as _, DispatchError}; use sp_io::{TestExternalities, hashing::{twox_64, twox_128, blake2_128}}; #[frame_support::pallet] pub mod pallet { use sp_std::any::TypeId; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; 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))] 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] 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::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!(); } } #[derive(codec::Encode, sp_runtime::RuntimeDebug)] #[cfg_attr(feature = "std", derive(codec::Decode))] pub enum InherentError { } impl sp_inherents::IsFatalError for InherentError { fn is_fatal_error(&self) -> bool { unimplemented!(); } } pub const INHERENT_IDENTIFIER: sp_inherents::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::*; use frame_system::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::hooks] impl, I: 'static> Hooks> for Pallet {} #[pallet::call] impl, I: 'static> Pallet {} #[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 = (); 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 = (); } 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::{Module, Call, Event}, Example: pallet::{Module, Call, Event, Config, Storage, Inherent, Origin, ValidateUnsigned}, Instance1Example: pallet::::{ Module, Call, Event, Config, Storage, Inherent, Origin, ValidateUnsigned }, Example2: pallet2::{Module, Call, Event, Config, Storage}, Instance1Example2: pallet2::::{Module, Call, 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::Module::::set_block_number(1); pallet::Call::::foo(3).dispatch_bypass_filter(None.into()).unwrap(); assert_eq!( frame_system::Module::::events()[0].event, Event::pallet(pallet::Event::Something(3)), ); }); TestExternalities::default().execute_with(|| { frame_system::Module::::set_block_number(1); pallet::Call::::foo(3).dispatch_bypass_filter(None.into()).unwrap(); assert_eq!( frame_system::Module::::events()[0].event, Event::pallet_Instance1(pallet::Event::Something(3)), ); }); } #[test] fn storage_expand() { use frame_support::pallet_prelude::*; use frame_support::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()); }); 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()); }); } #[test] fn pallet_hooks_expand() { TestExternalities::default().execute_with(|| { frame_system::Module::::set_block_number(1); assert_eq!(AllModules::on_initialize(1), 21); AllModules::on_finalize(1); assert_eq!(pallet::Pallet::::storage_version(), None); assert_eq!(pallet::Pallet::::storage_version(), None); assert_eq!(AllModules::on_runtime_upgrade(), 61); assert_eq!( pallet::Pallet::::storage_version(), Some(pallet::Pallet::::current_version()), ); assert_eq!( pallet::Pallet::::storage_version(), Some(pallet::Pallet::::current_version()), ); // The order is indeed reversed due to https://github.com/paritytech/substrate/issues/6280 assert_eq!( frame_system::Module::::events()[0].event, Event::pallet_Instance1(pallet::Event::Something(11)), ); assert_eq!( frame_system::Module::::events()[1].event, Event::pallet(pallet::Event::Something(10)), ); assert_eq!( frame_system::Module::::events()[2].event, Event::pallet_Instance1(pallet::Event::Something(21)), ); assert_eq!( frame_system::Module::::events()[3].event, Event::pallet(pallet::Event::Something(20)), ); assert_eq!( frame_system::Module::::events()[4].event, Event::pallet_Instance1(pallet::Event::Something(31)), ); assert_eq!( frame_system::Module::::events()[5].event, Event::pallet(pallet::Event::Something(30)), ); }) } #[test] fn pallet_on_genesis() { TestExternalities::default().execute_with(|| { assert_eq!(pallet::Pallet::::storage_version(), None); pallet::Pallet::::on_genesis(); assert_eq!( pallet::Pallet::::storage_version(), Some(pallet::Pallet::::current_version()), ); assert_eq!(pallet::Pallet::::storage_version(), None); pallet::Pallet::::on_genesis(); assert_eq!( pallet::Pallet::::storage_version(), Some(pallet::Pallet::::current_version()), ); }) } #[test] fn metadata() { use frame_metadata::*; use codec::{Decode, Encode}; 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![]), }, ]), })), 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::V12(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); }