// This file is part of Substrate. // Copyright (C) 2020-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. use frame_support::{ dispatch::{Parameter, UnfilteredDispatchable}, storage::unhashed, traits::{ ConstU32, GetCallName, GetStorageVersion, OnFinalize, OnGenesis, OnInitialize, OnRuntimeUpgrade, PalletInfoAccess, StorageVersion, }, weights::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays, RuntimeDbWeight}, }; use scale_info::{meta_type, TypeInfo}; use sp_io::{ hashing::{blake2_128, twox_128, twox_64}, TestExternalities, }; use sp_runtime::DispatchError; pub struct SomeType1; impl From for u64 { fn from(_t: SomeType1) -> Self { 0u64 } } pub struct SomeType2; impl From for u64 { fn from(_t: SomeType2) -> Self { 100u64 } } pub struct SomeType3; impl From for u64 { fn from(_t: SomeType3) -> Self { 0u64 } } pub struct SomeType4; impl From for u64 { fn from(_t: SomeType4) -> Self { 0u64 } } pub struct SomeType5; impl From for u64 { fn from(_t: SomeType5) -> Self { 0u64 } } pub struct SomeType6; impl From for u64 { fn from(_t: SomeType6) -> Self { 0u64 } } pub struct SomeType7; impl From for u64 { fn from(_t: SomeType7) -> Self { 0u64 } } pub trait SomeAssociation1 { type _1: Parameter + codec::MaxEncodedLen + TypeInfo; } impl SomeAssociation1 for u64 { type _1 = u64; } pub trait SomeAssociation2 { type _2: Parameter + codec::MaxEncodedLen + TypeInfo; } impl SomeAssociation2 for u64 { type _2 = u64; } #[frame_support::pallet] pub mod pallet { use super::{ SomeAssociation1, SomeAssociation2, SomeType1, SomeType2, SomeType3, SomeType4, SomeType5, SomeType6, SomeType7, StorageVersion, }; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use scale_info::TypeInfo; type BalanceOf = ::Balance; pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(10); #[pallet::config] pub trait Config: frame_system::Config where ::AccountId: From + SomeAssociation1, { /// Some comment /// Some comment #[pallet::constant] type MyGetParam: Get; /// Some comment /// Some comment #[pallet::constant] type MyGetParam2: Get; #[pallet::constant] type MyGetParam3: Get<::_1>; type Balance: Parameter + Default + TypeInfo; type Event: From> + IsType<::Event>; } #[pallet::extra_constants] impl Pallet where T::AccountId: From + SomeAssociation1 + From, { /// Some doc /// Some doc fn some_extra() -> T::AccountId { SomeType2.into() } /// Some doc fn some_extra_extra() -> T::AccountId { SomeType1.into() } /// Some doc #[pallet::constant_name(SomeExtraRename)] fn some_extra_rename() -> T::AccountId { SomeType1.into() } } #[pallet::pallet] #[pallet::generate_store(pub(crate) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); #[pallet::hooks] impl Hooks> for Pallet where T::AccountId: From + From + SomeAssociation1, { fn on_initialize(_: BlockNumberFor) -> Weight { let _ = T::AccountId::from(SomeType1); // Test for where clause let _ = T::AccountId::from(SomeType2); // Test for where clause Self::deposit_event(Event::Something(10)); 10 } fn on_finalize(_: BlockNumberFor) { let _ = T::AccountId::from(SomeType1); // Test for where clause let _ = T::AccountId::from(SomeType2); // Test for where clause Self::deposit_event(Event::Something(20)); } fn on_runtime_upgrade() -> Weight { let _ = T::AccountId::from(SomeType1); // Test for where clause let _ = T::AccountId::from(SomeType2); // Test for where clause Self::deposit_event(Event::Something(30)); 30 } fn integrity_test() { let _ = T::AccountId::from(SomeType1); // Test for where clause let _ = T::AccountId::from(SomeType2); // Test for where clause } } #[pallet::call] impl Pallet where T::AccountId: From + From + SomeAssociation1, { /// Doc comment put in metadata #[pallet::weight(Weight::from(*_foo))] pub fn foo( origin: OriginFor, #[pallet::compact] _foo: u32, _bar: u32, ) -> DispatchResultWithPostInfo { let _ = T::AccountId::from(SomeType1); // Test for where clause let _ = T::AccountId::from(SomeType3); // Test for where clause 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 { Self::deposit_event(Event::Something(0)); if foo == 0 { Err(Error::::InsufficientProposersBalance)?; } Ok(().into()) } // Test for DispatchResult return type #[pallet::weight(1)] pub fn foo_no_post_info(_origin: OriginFor) -> DispatchResult { Ok(()) } } #[pallet::error] pub enum Error { /// doc comment put into metadata InsufficientProposersBalance, } #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] pub enum Event where T::AccountId: SomeAssociation1 + From, { /// doc comment put in metadata Proposed(::AccountId), /// doc Spending(BalanceOf), Something(u32), SomethingElse(::_1), } #[pallet::storage] pub type ValueWhereClause where T::AccountId: SomeAssociation2, = StorageValue<_, ::_2>; #[pallet::storage] pub type Value = StorageValue; #[pallet::storage] #[pallet::storage_prefix = "Value2"] pub type RenamedValue = StorageValue; /// Test some doc #[pallet::type_value] pub fn MyDefault() -> u16 where T::AccountId: From + From + SomeAssociation1, { let _ = T::AccountId::from(SomeType7); // Test where clause works 4u16 } #[pallet::storage] pub type Map where T::AccountId: From, = StorageMap<_, Blake2_128Concat, u8, u16, ValueQuery, MyDefault>; #[pallet::storage] pub type Map2 = StorageMap>; #[pallet::storage] pub type DoubleMap = StorageDoubleMap<_, Blake2_128Concat, u8, Twox64Concat, u16, u32>; #[pallet::storage] pub type DoubleMap2 = StorageDoubleMap< Hasher1 = Twox64Concat, Key1 = u16, Hasher2 = Blake2_128Concat, Key2 = u32, Value = u64, MaxValues = ConstU32<5>, >; #[pallet::storage] #[pallet::getter(fn nmap)] pub type NMap = StorageNMap<_, storage::Key, u32>; #[pallet::storage] #[pallet::getter(fn nmap2)] pub type NMap2 = StorageNMap< Key = (NMapKey, NMapKey), Value = u64, MaxValues = ConstU32<11>, >; #[pallet::storage] #[pallet::getter(fn conditional_value)] #[cfg(feature = "conditional-storage")] pub type ConditionalValue = StorageValue<_, u32>; #[cfg(feature = "conditional-storage")] #[pallet::storage] #[pallet::getter(fn conditional_map)] pub type ConditionalMap = StorageMap<_, Twox64Concat, u16, u32, OptionQuery, GetDefault, ConstU32<12>>; #[cfg(feature = "conditional-storage")] #[pallet::storage] #[pallet::getter(fn conditional_double_map)] pub type ConditionalDoubleMap = StorageDoubleMap<_, Blake2_128Concat, u8, Twox64Concat, u16, u32>; #[cfg(feature = "conditional-storage")] #[pallet::storage] #[pallet::getter(fn conditional_nmap)] pub type ConditionalNMap = StorageNMap<_, (storage::Key, storage::Key), u32>; #[pallet::storage] #[pallet::storage_prefix = "RenamedCountedMap"] #[pallet::getter(fn counted_storage_map)] pub type SomeCountedStorageMap = CountedStorageMap; #[pallet::storage] #[pallet::unbounded] pub type Unbounded = StorageValue>; #[pallet::genesis_config] #[derive(Default)] pub struct GenesisConfig { _myfield: u32, } #[pallet::genesis_build] impl GenesisBuild for GenesisConfig where T::AccountId: From + SomeAssociation1 + From, { fn build(&self) { let _ = T::AccountId::from(SomeType1); // Test for where clause let _ = T::AccountId::from(SomeType4); // Test for where clause } } #[pallet::origin] #[derive( EqNoBound, RuntimeDebugNoBound, CloneNoBound, PartialEqNoBound, Encode, Decode, TypeInfo, )] pub struct Origin(PhantomData); #[pallet::validate_unsigned] impl ValidateUnsigned for Pallet where T::AccountId: From + SomeAssociation1 + From + From, { type Call = Call; fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity { let _ = T::AccountId::from(SomeType1); // Test for where clause let _ = T::AccountId::from(SomeType5); // Test for where clause if matches!(call, Call::foo_transactional { .. }) { return Ok(ValidTransaction::default()) } Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) } } #[pallet::inherent] impl ProvideInherent for Pallet where T::AccountId: From + SomeAssociation1 + From + From, { type Call = Call; type Error = InherentError; const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; fn create_inherent(_data: &InherentData) -> Option { let _ = T::AccountId::from(SomeType1); // Test for where clause let _ = T::AccountId::from(SomeType6); // Test for where clause Some(Call::foo_no_post_info {}) } fn is_inherent(call: &Self::Call) -> bool { matches!(call, Call::foo_no_post_info {} | Call::foo { .. }) } fn check_inherent(call: &Self::Call, _: &InherentData) -> Result<(), Self::Error> { match call { Call::foo_no_post_info {} => Ok(()), Call::foo { foo: 0, bar: 0 } => Err(InherentError::Fatal), Call::foo { .. } => Ok(()), _ => unreachable!("other calls are not inherents"), } } fn is_inherent_required(d: &InherentData) -> Result, Self::Error> { match d.get_data::(b"required") { Ok(Some(true)) => Ok(Some(InherentError::Fatal)), Ok(Some(false)) | Ok(None) => Ok(None), Err(_) => unreachable!("should not happen in tests"), } } } #[derive(codec::Encode, sp_runtime::RuntimeDebug)] #[cfg_attr(feature = "std", derive(codec::Decode))] pub enum InherentError { Fatal, } impl frame_support::inherent::IsFatalError for InherentError { fn is_fatal_error(&self) -> bool { matches!(self, InherentError::Fatal) } } pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"testpall"; } // Test that a pallet with non generic event and generic genesis_config is correctly handled // and that a pallet with the attribute without_storage_info is correctly handled. #[frame_support::pallet] pub mod pallet2 { use super::{SomeAssociation1, SomeType1}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; #[pallet::config] pub trait Config: frame_system::Config where ::AccountId: From + SomeAssociation1, { type Event: From + IsType<::Event>; } #[pallet::pallet] #[pallet::generate_store(pub(crate) trait Store)] #[pallet::without_storage_info] pub struct Pallet(_); #[pallet::hooks] impl Hooks> for Pallet where T::AccountId: From + SomeAssociation1, { fn on_initialize(_: BlockNumberFor) -> Weight { Self::deposit_event(Event::Something(11)); 0 } fn on_finalize(_: BlockNumberFor) { Self::deposit_event(Event::Something(21)); } fn on_runtime_upgrade() -> Weight { Self::deposit_event(Event::Something(31)); 0 } } #[pallet::call] impl Pallet where T::AccountId: From + SomeAssociation1 {} #[pallet::storage] pub type SomeValue = StorageValue<_, Vec>; #[pallet::storage] pub type SomeCountedStorageMap = CountedStorageMap; #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] pub enum Event { /// Something Something(u32), } #[pallet::genesis_config] pub struct GenesisConfig where T::AccountId: From + SomeAssociation1, { phantom: PhantomData, } impl Default for GenesisConfig where T::AccountId: From + SomeAssociation1, { fn default() -> Self { GenesisConfig { phantom: Default::default() } } } #[pallet::genesis_build] impl GenesisBuild for GenesisConfig where T::AccountId: From + SomeAssociation1, { fn build(&self) {} } } /// Test that the supertrait check works when we pass some parameter to the `frame_system::Config`. #[frame_support::pallet] pub mod pallet3 { #[pallet::config] pub trait Config: frame_system::Config {} #[pallet::pallet] pub struct Pallet(_); } #[frame_support::pallet] pub mod pallet4 { #[pallet::config] pub trait Config: frame_system::Config {} #[pallet::pallet] pub struct Pallet(_); #[pallet::call] impl Pallet {} } frame_support::parameter_types!( pub const MyGetParam3: u32 = 12; ); 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 = ConstU32<250>; type BlockWeights = (); type BlockLength = (); type DbWeight = (); type Version = (); type PalletInfo = PalletInfo; type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); type SS58Prefix = (); type OnSetCode = (); type MaxConsumers = ConstU32<16>; } impl pallet::Config for Runtime { type Event = Event; type MyGetParam = ConstU32<10>; type MyGetParam2 = ConstU32<11>; type MyGetParam3 = MyGetParam3; type Balance = u64; } impl pallet2::Config for Runtime { type Event = Event; } impl pallet4::Config for Runtime {} 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 { Pallet, Storage }, Example: pallet, Example2: pallet2 exclude_parts { Call }, Example4: pallet4 use_parts { Call }, } ); // Test that the part `Call` is excluded from Example2 and included in Example4. fn _ensure_call_is_correctly_excluded_and_included(call: Call) { match call { Call::System(_) | Call::Example(_) | Call::Example4(_) => (), } } #[test] fn transactional_works() { TestExternalities::default().execute_with(|| { frame_system::Pallet::::set_block_number(1); pallet::Call::::foo_transactional { foo: 0 } .dispatch_bypass_filter(None.into()) .err() .unwrap(); assert!(frame_system::Pallet::::events().is_empty()); pallet::Call::::foo_transactional { foo: 1 } .dispatch_bypass_filter(None.into()) .unwrap(); assert_eq!( frame_system::Pallet::::events() .iter() .map(|e| &e.event) .collect::>(), vec![&Event::Example(pallet::Event::Something(0))], ); }) } #[test] fn call_expand() { let call_foo = pallet::Call::::foo { foo: 3, bar: 0 }; 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", "foo_no_post_info"], ); } #[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") }, ); } #[test] fn instance_expand() { // Assert same type. let _: pallet::__InherentHiddenInstance = (); } #[test] fn inherent_expand() { use frame_support::{ inherent::{BlockT, InherentData}, traits::EnsureInherentsAreFirst, }; use sp_core::Hasher; use sp_runtime::{ traits::{BlakeTwo256, Header}, Digest, }; let inherents = InherentData::new().create_extrinsics(); let expected = vec![UncheckedExtrinsic { function: Call::Example(pallet::Call::foo_no_post_info {}), signature: None, }]; assert_eq!(expected, inherents); let block = Block::new( Header::new( 1, BlakeTwo256::hash(b"test"), BlakeTwo256::hash(b"test"), BlakeTwo256::hash(b"test"), Digest::default(), ), vec![ UncheckedExtrinsic { function: Call::Example(pallet::Call::foo_no_post_info {}), signature: None, }, UncheckedExtrinsic { function: Call::Example(pallet::Call::foo { foo: 1, bar: 0 }), signature: None, }, ], ); assert!(InherentData::new().check_extrinsics(&block).ok()); let block = Block::new( Header::new( 1, BlakeTwo256::hash(b"test"), BlakeTwo256::hash(b"test"), BlakeTwo256::hash(b"test"), Digest::default(), ), vec![ UncheckedExtrinsic { function: Call::Example(pallet::Call::foo_no_post_info {}), signature: None, }, UncheckedExtrinsic { function: Call::Example(pallet::Call::foo { foo: 0, bar: 0 }), signature: None, }, ], ); assert!(InherentData::new().check_extrinsics(&block).fatal_error()); let block = Block::new( Header::new( 1, BlakeTwo256::hash(b"test"), BlakeTwo256::hash(b"test"), BlakeTwo256::hash(b"test"), Digest::default(), ), vec![UncheckedExtrinsic { function: Call::Example(pallet::Call::foo_transactional { foo: 0 }), signature: None, }], ); let mut inherent = InherentData::new(); inherent.put_data(*b"required", &true).unwrap(); assert!(inherent.check_extrinsics(&block).fatal_error()); let block = Block::new( Header::new( 1, BlakeTwo256::hash(b"test"), BlakeTwo256::hash(b"test"), BlakeTwo256::hash(b"test"), Digest::default(), ), vec![UncheckedExtrinsic { function: Call::Example(pallet::Call::foo_no_post_info {}), signature: Some((1, (), ())), }], ); let mut inherent = InherentData::new(); inherent.put_data(*b"required", &true).unwrap(); assert!(inherent.check_extrinsics(&block).fatal_error()); let block = Block::new( Header::new( 1, BlakeTwo256::hash(b"test"), BlakeTwo256::hash(b"test"), BlakeTwo256::hash(b"test"), Digest::default(), ), vec![ UncheckedExtrinsic { function: Call::Example(pallet::Call::foo { foo: 1, bar: 1 }), signature: None, }, UncheckedExtrinsic { function: Call::Example(pallet::Call::foo_transactional { foo: 0 }), signature: None, }, ], ); assert!(Runtime::ensure_inherents_are_first(&block).is_ok()); let block = Block::new( Header::new( 1, BlakeTwo256::hash(b"test"), BlakeTwo256::hash(b"test"), BlakeTwo256::hash(b"test"), Digest::default(), ), vec![ UncheckedExtrinsic { function: Call::Example(pallet::Call::foo { foo: 1, bar: 1 }), signature: None, }, UncheckedExtrinsic { function: Call::Example(pallet::Call::foo_transactional { foo: 0 }), signature: None, }, UncheckedExtrinsic { function: Call::Example(pallet::Call::foo_no_post_info {}), signature: None, }, ], ); assert_eq!(Runtime::ensure_inherents_are_first(&block).err().unwrap(), 2); let block = Block::new( Header::new( 1, BlakeTwo256::hash(b"test"), BlakeTwo256::hash(b"test"), BlakeTwo256::hash(b"test"), Digest::default(), ), vec![ UncheckedExtrinsic { function: Call::Example(pallet::Call::foo { foo: 1, bar: 1 }), signature: None, }, UncheckedExtrinsic { function: Call::Example(pallet::Call::foo { foo: 1, bar: 0 }), signature: Some((1, (), ())), }, UncheckedExtrinsic { function: Call::Example(pallet::Call::foo_no_post_info {}), signature: None, }, ], ); assert_eq!(Runtime::ensure_inherents_are_first(&block).err().unwrap(), 2); } #[test] fn validate_unsigned_expand() { use frame_support::pallet_prelude::{ InvalidTransaction, TransactionSource, TransactionValidityError, ValidTransaction, ValidateUnsigned, }; let call = pallet::Call::::foo_no_post_info {}; let validity = pallet::Pallet::validate_unsigned(TransactionSource::Local, &call).unwrap_err(); assert_eq!(validity, TransactionValidityError::Invalid(InvalidTransaction::Call)); let call = pallet::Call::::foo_transactional { foo: 0 }; let validity = pallet::Pallet::validate_unsigned(TransactionSource::External, &call).unwrap(); assert_eq!(validity, ValidTransaction::default()); } #[test] fn trait_store_expand() { TestExternalities::default().execute_with(|| { as pallet::Store>::Value::get(); as pallet::Store>::Map::get(1); as pallet::Store>::DoubleMap::get(1, 2); }) } #[test] fn pallet_expand_deposit_event() { TestExternalities::default().execute_with(|| { frame_system::Pallet::::set_block_number(1); pallet::Call::::foo { foo: 3, bar: 0 } .dispatch_bypass_filter(None.into()) .unwrap(); assert_eq!( frame_system::Pallet::::events()[0].event, Event::Example(pallet::Event::Something(3)), ); }) } #[test] fn pallet_new_call_variant() { pallet::Call::::new_call_variant_foo(3, 4); } #[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(|| { pallet::Value::::put(1); let k = [twox_128(b"Example"), twox_128(b"Value")].concat(); assert_eq!(unhashed::get::(&k), Some(1u32)); pallet::RenamedValue::::put(2); let k = [twox_128(b"Example"), twox_128(b"Value2")].concat(); assert_eq!(unhashed::get::(&k), Some(2)); pallet::Map::::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()); pallet::Map2::::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()); pallet::DoubleMap::::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()); pallet::DoubleMap2::::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()); pallet::NMap::::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()); pallet::NMap2::::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()); #[cfg(feature = "conditional-storage")] { pallet::ConditionalValue::::put(1); pallet::ConditionalMap::::insert(1, 2); pallet::ConditionalDoubleMap::::insert(1, 2, 3); pallet::ConditionalNMap::::insert((1, 2), 3); } pallet::SomeCountedStorageMap::::insert(1, 2); let mut k = [twox_128(b"Example"), twox_128(b"RenamedCountedMap")].concat(); k.extend(1u8.using_encoded(twox_64_concat)); assert_eq!(unhashed::get::(&k), Some(2u32)); let k = [twox_128(b"Example"), twox_128(b"CounterForRenamedCountedMap")].concat(); assert_eq!(unhashed::get::(&k), Some(1u32)); pallet::Unbounded::::put(vec![1, 2]); let k = [twox_128(b"Example"), twox_128(b"Unbounded")].concat(); assert_eq!(unhashed::get::>(&k), Some(vec![1, 2])); }) } #[test] fn pallet_hooks_expand() { TestExternalities::default().execute_with(|| { frame_system::Pallet::::set_block_number(1); assert_eq!(AllPalletsWithoutSystem::on_initialize(1), 10); AllPalletsWithoutSystem::on_finalize(1); assert_eq!(AllPalletsWithoutSystem::on_runtime_upgrade(), 30); assert_eq!( frame_system::Pallet::::events()[0].event, Event::Example(pallet::Event::Something(10)), ); assert_eq!( frame_system::Pallet::::events()[1].event, Event::Example2(pallet2::Event::Something(11)), ); assert_eq!( frame_system::Pallet::::events()[2].event, Event::Example(pallet::Event::Something(20)), ); assert_eq!( frame_system::Pallet::::events()[3].event, Event::Example2(pallet2::Event::Something(21)), ); assert_eq!( frame_system::Pallet::::events()[4].event, Event::Example(pallet::Event::Something(30)), ); assert_eq!( frame_system::Pallet::::events()[5].event, Event::Example2(pallet2::Event::Something(31)), ); }) } #[test] fn all_pallets_type_reversed_order_is_correct() { TestExternalities::default().execute_with(|| { frame_system::Pallet::::set_block_number(1); #[allow(deprecated)] { assert_eq!(AllPalletsWithoutSystemReversed::on_initialize(1), 10); AllPalletsWithoutSystemReversed::on_finalize(1); assert_eq!(AllPalletsWithoutSystemReversed::on_runtime_upgrade(), 30); } assert_eq!( frame_system::Pallet::::events()[0].event, Event::Example2(pallet2::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::Example2(pallet2::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::Example2(pallet2::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(|| { assert_eq!(pallet::Pallet::::on_chain_storage_version(), StorageVersion::new(0)); pallet::Pallet::::on_genesis(); assert_eq!( pallet::Pallet::::current_storage_version(), pallet::Pallet::::on_chain_storage_version(), ); }) } #[test] fn migrate_from_pallet_version_to_storage_version() { const PALLET_VERSION_STORAGE_KEY_POSTFIX: &[u8] = b":__PALLET_VERSION__:"; fn pallet_version_key(name: &str) -> [u8; 32] { frame_support::storage::storage_prefix(name.as_bytes(), PALLET_VERSION_STORAGE_KEY_POSTFIX) } TestExternalities::default().execute_with(|| { // Insert some fake pallet versions sp_io::storage::set(&pallet_version_key(Example::name()), &[1, 2, 3]); sp_io::storage::set(&pallet_version_key(Example2::name()), &[1, 2, 3]); sp_io::storage::set(&pallet_version_key(System::name()), &[1, 2, 3]); // Check that everyone currently is at version 0 assert_eq!(Example::on_chain_storage_version(), StorageVersion::new(0)); assert_eq!(Example2::on_chain_storage_version(), StorageVersion::new(0)); assert_eq!(System::on_chain_storage_version(), StorageVersion::new(0)); let db_weight = RuntimeDbWeight { read: 0, write: 5 }; let weight = frame_support::migrations::migrate_from_pallet_version_to_storage_version::< AllPalletsWithSystem, >(&db_weight); // 4 pallets, 2 writes and every write costs 5 weight. assert_eq!(4 * 2 * 5, weight); // All pallet versions should be removed assert!(sp_io::storage::get(&pallet_version_key(Example::name())).is_none()); assert!(sp_io::storage::get(&pallet_version_key(Example2::name())).is_none()); assert!(sp_io::storage::get(&pallet_version_key(System::name())).is_none()); assert_eq!(Example::on_chain_storage_version(), pallet::STORAGE_VERSION); assert_eq!(Example2::on_chain_storage_version(), StorageVersion::new(0)); assert_eq!(System::on_chain_storage_version(), StorageVersion::new(0)); }); } #[test] fn metadata() { use frame_support::metadata::*; fn maybe_docs(doc: Vec<&'static str>) -> Vec<&'static str> { if cfg!(feature = "no-metadata-docs") { vec![] } else { doc } } let pallets = vec![ PalletMetadata { index: 1, name: "Example", storage: Some(PalletStorageMetadata { prefix: "Example", entries: vec![ StorageEntryMetadata { name: "ValueWhereClause", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Plain(meta_type::()), default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "Value", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Plain(meta_type::()), default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "Value2", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Plain(meta_type::()), default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "Map", modifier: StorageEntryModifier::Default, ty: StorageEntryType::Map { key: meta_type::(), value: meta_type::(), hashers: vec![StorageHasher::Blake2_128Concat], }, default: vec![4, 0], docs: vec![], }, StorageEntryMetadata { name: "Map2", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { key: meta_type::(), value: meta_type::(), hashers: vec![StorageHasher::Twox64Concat], }, default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "DoubleMap", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { value: meta_type::(), hashers: vec![ StorageHasher::Blake2_128Concat, StorageHasher::Twox64Concat, ], key: meta_type::<(u8, u16)>(), }, default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "DoubleMap2", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { value: meta_type::(), key: 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: meta_type::(), hashers: vec![StorageHasher::Blake2_128Concat], value: meta_type::(), }, default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "NMap2", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { key: meta_type::<(u16, u32)>(), hashers: vec![ StorageHasher::Twox64Concat, StorageHasher::Blake2_128Concat, ], value: meta_type::(), }, default: vec![0], docs: vec![], }, #[cfg(feature = "conditional-storage")] StorageEntryMetadata { name: "ConditionalValue", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Plain(meta_type::()), default: vec![0], docs: vec![], }, #[cfg(feature = "conditional-storage")] StorageEntryMetadata { name: "ConditionalMap", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { key: meta_type::(), value: meta_type::(), hashers: vec![StorageHasher::Twox64Concat], }, default: vec![0], docs: vec![], }, #[cfg(feature = "conditional-storage")] StorageEntryMetadata { name: "ConditionalDoubleMap", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { value: meta_type::(), key: meta_type::<(u8, u16)>(), hashers: vec![ StorageHasher::Blake2_128Concat, StorageHasher::Twox64Concat, ], }, default: vec![0], docs: vec![], }, #[cfg(feature = "conditional-storage")] StorageEntryMetadata { name: "ConditionalNMap", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { key: meta_type::<(u8, u16)>(), hashers: vec![ StorageHasher::Blake2_128Concat, StorageHasher::Twox64Concat, ], value: meta_type::(), }, default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "RenamedCountedMap", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { hashers: vec![StorageHasher::Twox64Concat], key: meta_type::(), value: meta_type::(), }, default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "CounterForRenamedCountedMap", modifier: StorageEntryModifier::Default, ty: StorageEntryType::Plain(meta_type::()), default: vec![0, 0, 0, 0], docs: maybe_docs(vec!["Counter for the related counted storage map"]), }, StorageEntryMetadata { name: "Unbounded", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Plain(meta_type::>()), default: vec![0], docs: vec![], }, ], }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![ PalletConstantMetadata { name: "MyGetParam", ty: meta_type::(), value: vec![10, 0, 0, 0], docs: maybe_docs(vec![" Some comment", " Some comment"]), }, PalletConstantMetadata { name: "MyGetParam2", ty: meta_type::(), value: vec![11, 0, 0, 0], docs: maybe_docs(vec![" Some comment", " Some comment"]), }, PalletConstantMetadata { name: "MyGetParam3", ty: meta_type::(), value: vec![12, 0, 0, 0, 0, 0, 0, 0], docs: vec![], }, PalletConstantMetadata { name: "some_extra", ty: meta_type::(), value: vec![100, 0, 0, 0, 0, 0, 0, 0], docs: maybe_docs(vec![" Some doc", " Some doc"]), }, PalletConstantMetadata { name: "some_extra_extra", ty: meta_type::(), value: vec![0, 0, 0, 0, 0, 0, 0, 0], docs: maybe_docs(vec![" Some doc"]), }, PalletConstantMetadata { name: "SomeExtraRename", ty: meta_type::(), value: vec![0, 0, 0, 0, 0, 0, 0, 0], docs: maybe_docs(vec![" Some doc"]), }, ], error: Some(PalletErrorMetadata { ty: meta_type::>() }), }, PalletMetadata { index: 2, name: "Example2", storage: Some(PalletStorageMetadata { prefix: "Example2", entries: vec![ StorageEntryMetadata { name: "SomeValue", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Plain(meta_type::>()), default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "SomeCountedStorageMap", modifier: StorageEntryModifier::Optional, ty: StorageEntryType::Map { hashers: vec![StorageHasher::Twox64Concat], key: meta_type::(), value: meta_type::(), }, default: vec![0], docs: vec![], }, StorageEntryMetadata { name: "CounterForSomeCountedStorageMap", modifier: StorageEntryModifier::Default, ty: StorageEntryType::Plain(meta_type::()), default: vec![0, 0, 0, 0], docs: maybe_docs(vec!["Counter for the related counted storage map"]), }, ], }), calls: None, event: Some(PalletEventMetadata { ty: meta_type::() }), constants: vec![], error: None, }, ]; let empty_doc = pallets[0].event.as_ref().unwrap().ty.type_info().docs().is_empty() && pallets[0].error.as_ref().unwrap().ty.type_info().docs().is_empty() && pallets[0].calls.as_ref().unwrap().ty.type_info().docs().is_empty(); if cfg!(feature = "no-metadata-docs") { assert!(empty_doc) } else { assert!(!empty_doc) } 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 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, expected_metadata.pallets); } #[test] fn test_pallet_info_access() { assert_eq!(::name(), "System"); assert_eq!(::name(), "Example"); assert_eq!(::name(), "Example2"); assert_eq!(::index(), 0); assert_eq!(::index(), 1); assert_eq!(::index(), 2); } #[test] fn test_storage_info() { use frame_support::{ storage::storage_prefix as prefix, traits::{StorageInfo, StorageInfoTrait}, }; assert_eq!( Example::storage_info(), vec![ StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"ValueWhereClause".to_vec(), prefix: prefix(b"Example", b"ValueWhereClause").to_vec(), max_values: Some(1), max_size: Some(8), }, StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"Value".to_vec(), prefix: prefix(b"Example", b"Value").to_vec(), max_values: Some(1), max_size: Some(4), }, StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"Value2".to_vec(), prefix: prefix(b"Example", b"Value2").to_vec(), max_values: Some(1), max_size: Some(8), }, StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"Map".to_vec(), prefix: prefix(b"Example", b"Map").to_vec(), max_values: None, max_size: Some(3 + 16), }, StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"Map2".to_vec(), prefix: prefix(b"Example", b"Map2").to_vec(), max_values: Some(3), max_size: Some(6 + 8), }, StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"DoubleMap".to_vec(), prefix: prefix(b"Example", b"DoubleMap").to_vec(), max_values: None, max_size: Some(7 + 16 + 8), }, StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"DoubleMap2".to_vec(), prefix: prefix(b"Example", b"DoubleMap2").to_vec(), max_values: Some(5), max_size: Some(14 + 8 + 16), }, StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"NMap".to_vec(), prefix: prefix(b"Example", b"NMap").to_vec(), max_values: None, max_size: Some(5 + 16), }, StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"NMap2".to_vec(), prefix: prefix(b"Example", b"NMap2").to_vec(), max_values: Some(11), max_size: Some(14 + 8 + 16), }, #[cfg(feature = "conditional-storage")] { StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"ConditionalValue".to_vec(), prefix: prefix(b"Example", b"ConditionalValue").to_vec(), max_values: Some(1), max_size: Some(4), } }, #[cfg(feature = "conditional-storage")] { StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"ConditionalMap".to_vec(), prefix: prefix(b"Example", b"ConditionalMap").to_vec(), max_values: Some(12), max_size: Some(6 + 8), } }, #[cfg(feature = "conditional-storage")] { StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"ConditionalDoubleMap".to_vec(), prefix: prefix(b"Example", b"ConditionalDoubleMap").to_vec(), max_values: None, max_size: Some(7 + 16 + 8), } }, #[cfg(feature = "conditional-storage")] { StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"ConditionalNMap".to_vec(), prefix: prefix(b"Example", b"ConditionalNMap").to_vec(), max_values: None, max_size: Some(7 + 16 + 8), } }, StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"RenamedCountedMap".to_vec(), prefix: prefix(b"Example", b"RenamedCountedMap").to_vec(), max_values: None, max_size: Some(1 + 4 + 8), }, StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"CounterForRenamedCountedMap".to_vec(), prefix: prefix(b"Example", b"CounterForRenamedCountedMap").to_vec(), max_values: Some(1), max_size: Some(4), }, StorageInfo { pallet_name: b"Example".to_vec(), storage_name: b"Unbounded".to_vec(), prefix: prefix(b"Example", b"Unbounded").to_vec(), max_values: Some(1), max_size: None, }, ], ); assert_eq!( Example2::storage_info(), vec![ StorageInfo { pallet_name: b"Example2".to_vec(), storage_name: b"SomeValue".to_vec(), prefix: prefix(b"Example2", b"SomeValue").to_vec(), max_values: Some(1), max_size: None, }, StorageInfo { pallet_name: b"Example2".to_vec(), storage_name: b"SomeCountedStorageMap".to_vec(), prefix: prefix(b"Example2", b"SomeCountedStorageMap").to_vec(), max_values: None, max_size: None, }, StorageInfo { pallet_name: b"Example2".to_vec(), storage_name: b"CounterForSomeCountedStorageMap".to_vec(), prefix: prefix(b"Example2", b"CounterForSomeCountedStorageMap").to_vec(), max_values: Some(1), max_size: Some(4), }, ], ); } #[test] fn assert_type_all_pallets_reversed_with_system_first_is_correct() { // Just ensure the 2 types are same. fn _a(_t: AllPalletsReversedWithSystemFirst) {} fn _b(t: (System, (Example4, (Example2, (Example,))))) { _a(t) } } #[test] fn assert_type_all_pallets_with_system_is_correct() { // Just ensure the 2 types are same. fn _a(_t: AllPalletsWithSystem) {} fn _b(t: (System, (Example, (Example2, (Example4,))))) { _a(t) } } #[test] fn assert_type_all_pallets_without_system_is_correct() { // Just ensure the 2 types are same. fn _a(_t: AllPalletsWithoutSystem) {} fn _b(t: (Example, (Example2, (Example4,)))) { _a(t) } } #[test] fn assert_type_all_pallets_with_system_reversed_is_correct() { // Just ensure the 2 types are same. fn _a(_t: AllPalletsWithSystemReversed) {} fn _b(t: (Example4, (Example2, (Example, (System,))))) { _a(t) } } #[test] fn assert_type_all_pallets_without_system_reversed_is_correct() { // Just ensure the 2 types are same. fn _a(_t: AllPalletsWithoutSystemReversed) {} fn _b(t: (Example4, (Example2, (Example,)))) { _a(t) } }