// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Polkadot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . //! Parachain runtime mock. use codec::{Decode, Encode}; use core::marker::PhantomData; use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::{ContainsPair, EnsureOrigin, EnsureOriginWithArg, Everything, EverythingBut, Nothing}, weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, }; use frame_system::EnsureRoot; use sp_core::{ConstU32, H256}; use sp_runtime::{ traits::{Get, Hash, IdentityLookup}, AccountId32, }; use sp_std::prelude::*; use pallet_xcm::XcmPassthrough; use polkadot_core_primitives::BlockNumber as RelayBlockNumber; use polkadot_parachain_primitives::primitives::{ DmpMessageHandler, Id as ParaId, Sibling, XcmpMessageFormat, XcmpMessageHandler, }; use xcm::{latest::prelude::*, VersionedXcm}; use xcm_builder::{ Account32Hash, AccountId32Aliases, AllowUnpaidExecutionFrom, ConvertedConcreteId, EnsureDecodableXcm, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, IsConcrete, NativeAsset, NoChecking, NonFungiblesAdapter, ParentIsPreset, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, }; use xcm_executor::{ traits::{ConvertLocation, JustTry}, Config, XcmExecutor, }; pub type SovereignAccountOf = ( SiblingParachainConvertsVia, AccountId32Aliases, ParentIsPreset, ); pub type AccountId = AccountId32; pub type Balance = u128; parameter_types! { pub const BlockHashCount: u64 = 250; } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type Nonce = u64; type Hash = H256; type Hashing = ::sp_runtime::traits::BlakeTwo256; type AccountId = AccountId; type Lookup = IdentityLookup; type Block = Block; type RuntimeEvent = RuntimeEvent; type BlockHashCount = BlockHashCount; type BlockWeights = (); type BlockLength = (); type Version = (); type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type DbWeight = (); type BaseCallFilter = Everything; type SystemWeightInfo = (); type SS58Prefix = (); type OnSetCode = (); type MaxConsumers = ConstU32<16>; } parameter_types! { pub ExistentialDeposit: Balance = 1; pub const MaxLocks: u32 = 50; pub const MaxReserves: u32 = 50; } impl pallet_balances::Config for Runtime { type MaxLocks = MaxLocks; type Balance = Balance; type RuntimeEvent = RuntimeEvent; type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); type MaxReserves = MaxReserves; type ReserveIdentifier = [u8; 8]; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; } #[cfg(feature = "runtime-benchmarks")] pub struct UniquesHelper; #[cfg(feature = "runtime-benchmarks")] impl pallet_uniques::BenchmarkHelper for UniquesHelper { fn collection(i: u16) -> Location { GeneralIndex(i as u128).into() } fn item(i: u16) -> AssetInstance { AssetInstance::Index(i as u128) } } impl pallet_uniques::Config for Runtime { type RuntimeEvent = RuntimeEvent; type CollectionId = Location; type ItemId = AssetInstance; type Currency = Balances; type CreateOrigin = ForeignCreators; type ForceOrigin = frame_system::EnsureRoot; type CollectionDeposit = frame_support::traits::ConstU128<1_000>; type ItemDeposit = frame_support::traits::ConstU128<1_000>; type MetadataDepositBase = frame_support::traits::ConstU128<1_000>; type AttributeDepositBase = frame_support::traits::ConstU128<1_000>; type DepositPerByte = frame_support::traits::ConstU128<1>; type StringLimit = ConstU32<64>; type KeyLimit = ConstU32<64>; type ValueLimit = ConstU32<128>; type Locker = (); type WeightInfo = (); #[cfg(feature = "runtime-benchmarks")] type Helper = UniquesHelper; } // `EnsureOriginWithArg` impl for `CreateOrigin` which allows only XCM origins // which are locations containing the class location. pub struct ForeignCreators; impl EnsureOriginWithArg for ForeignCreators { type Success = AccountId; fn try_origin( o: RuntimeOrigin, a: &Location, ) -> sp_std::result::Result { let origin_location = pallet_xcm::EnsureXcm::::try_origin(o.clone())?; if !a.starts_with(&origin_location) { return Err(o) } SovereignAccountOf::convert_location(&origin_location).ok_or(o) } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin(a: &Location) -> Result { Ok(pallet_xcm::Origin::Xcm(a.clone()).into()) } } parameter_types! { pub const ReservedXcmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); pub const ReservedDmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); } parameter_types! { pub const KsmLocation: Location = Location::parent(); pub const RelayNetwork: NetworkId = NetworkId::Kusama; pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); } pub type LocationToAccountId = ( ParentIsPreset, SiblingParachainConvertsVia, AccountId32Aliases, Account32Hash<(), AccountId>, ); pub type XcmOriginToCallOrigin = ( SovereignSignedViaLocation, SignedAccountId32AsNative, XcmPassthrough, ); parameter_types! { pub const UnitWeightCost: Weight = Weight::from_parts(1, 1); pub KsmPerSecondPerByte: (AssetId, u128, u128) = (AssetId(Parent.into()), 1, 1); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; pub ForeignPrefix: Location = (Parent,).into(); } pub type LocalAssetTransactor = ( FungibleAdapter, LocationToAccountId, AccountId, ()>, NonFungiblesAdapter< ForeignUniques, ConvertedConcreteId, SovereignAccountOf, AccountId, NoChecking, (), >, ); pub type XcmRouter = EnsureDecodableXcm>; pub type Barrier = AllowUnpaidExecutionFrom; parameter_types! { pub NftCollectionOne: AssetFilter = Wild(AllOf { fun: WildNonFungible, id: AssetId((Parent, GeneralIndex(1)).into()) }); pub NftCollectionOneForRelay: (AssetFilter, Location) = (NftCollectionOne::get(), (Parent,).into()); } pub type TrustedTeleporters = xcm_builder::Case; pub type TrustedReserves = EverythingBut>; pub struct XcmConfig; impl Config for XcmConfig { type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; type AssetTransactor = LocalAssetTransactor; type OriginConverter = XcmOriginToCallOrigin; type IsReserve = (NativeAsset, TrustedReserves); type IsTeleporter = TrustedTeleporters; type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = FixedRateOfFungible; type ResponseHandler = (); type AssetTrap = (); type AssetLocker = PolkadotXcm; type AssetExchanger = (); type AssetClaims = (); type SubscriptionService = (); type PalletInstancesInfo = (); type FeeManager = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; type Aliasers = Nothing; type TransactionalProcessor = FrameTransactionalProcessor; type HrmpNewChannelOpenRequestHandler = (); type HrmpChannelAcceptedHandler = (); type HrmpChannelClosingHandler = (); } #[frame_support::pallet] pub mod mock_msg_queue { use super::*; use frame_support::pallet_prelude::*; #[pallet::config] pub trait Config: frame_system::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; type XcmExecutor: ExecuteXcm; } #[pallet::call] impl Pallet {} #[pallet::pallet] #[pallet::without_storage_info] pub struct Pallet(_); #[pallet::storage] #[pallet::getter(fn parachain_id)] pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; #[pallet::storage] #[pallet::getter(fn received_dmp)] /// A queue of received DMP messages pub(super) type ReceivedDmp = StorageValue<_, Vec>, ValueQuery>; impl Get for Pallet { fn get() -> ParaId { Self::parachain_id() } } pub type MessageId = [u8; 32]; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { // XCMP /// Some XCM was executed OK. Success(Option), /// Some XCM failed. Fail(Option, XcmError), /// Bad XCM version used. BadVersion(Option), /// Bad XCM format used. BadFormat(Option), // DMP /// Downward message is invalid XCM. InvalidFormat(MessageId), /// Downward message is unsupported version of XCM. UnsupportedVersion(MessageId), /// Downward message executed with the given outcome. ExecutedDownward(MessageId, Outcome), } impl Pallet { pub fn set_para_id(para_id: ParaId) { ParachainId::::put(para_id); } fn handle_xcmp_message( sender: ParaId, _sent_at: RelayBlockNumber, xcm: VersionedXcm, max_weight: Weight, ) -> Result { let hash = Encode::using_encoded(&xcm, T::Hashing::hash); let mut message_hash = Encode::using_encoded(&xcm, sp_io::hashing::blake2_256); let (result, event) = match Xcm::::try_from(xcm) { Ok(xcm) => { let location = (Parent, Parachain(sender.into())); match T::XcmExecutor::prepare_and_execute( location, xcm, &mut message_hash, max_weight, Weight::zero(), ) { Outcome::Error { error } => (Err(error), Event::Fail(Some(hash), error)), Outcome::Complete { used } => (Ok(used), Event::Success(Some(hash))), // As far as the caller is concerned, this was dispatched without error, so // we just report the weight used. Outcome::Incomplete { used, error } => (Ok(used), Event::Fail(Some(hash), error)), } }, Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))), }; Self::deposit_event(event); result } } impl XcmpMessageHandler for Pallet { fn handle_xcmp_messages<'a, I: Iterator>( iter: I, max_weight: Weight, ) -> Weight { for (sender, sent_at, data) in iter { let mut data_ref = data; let _ = XcmpMessageFormat::decode(&mut data_ref) .expect("Simulator encodes with versioned xcm format; qed"); let mut remaining_fragments = data_ref; while !remaining_fragments.is_empty() { if let Ok(xcm) = VersionedXcm::::decode(&mut remaining_fragments) { let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight); } else { debug_assert!(false, "Invalid incoming XCMP message data"); } } } max_weight } } impl DmpMessageHandler for Pallet { fn handle_dmp_messages( iter: impl Iterator)>, limit: Weight, ) -> Weight { for (_i, (_sent_at, data)) in iter.enumerate() { let mut id = sp_io::hashing::blake2_256(&data[..]); let maybe_versioned = VersionedXcm::::decode(&mut &data[..]); match maybe_versioned { Err(_) => { Self::deposit_event(Event::InvalidFormat(id)); }, Ok(versioned) => match Xcm::try_from(versioned) { Err(()) => Self::deposit_event(Event::UnsupportedVersion(id)), Ok(x) => { let outcome = T::XcmExecutor::prepare_and_execute( Parent, x.clone(), &mut id, limit, Weight::zero(), ); >::append(x); Self::deposit_event(Event::ExecutedDownward(id, outcome)); }, }, } } limit } } } impl mock_msg_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; } pub type LocalOriginToLocation = SignedToAccountId32; pub struct TrustedLockerCase(PhantomData); impl> ContainsPair for TrustedLockerCase { fn contains(origin: &Location, asset: &Asset) -> bool { let (o, a) = T::get(); a.matches(asset) && &o == origin } } parameter_types! { pub RelayTokenForRelay: (Location, AssetFilter) = (Parent.into(), Wild(AllOf { id: AssetId(Parent.into()), fun: WildFungible })); } pub type TrustedLockers = TrustedLockerCase; impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type SendXcmOrigin = EnsureXcmOrigin; type XcmRouter = XcmRouter; type ExecuteXcmOrigin = EnsureXcmOrigin; type XcmExecuteFilter = Everything; type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Nothing; type XcmReserveTransferFilter = Everything; type Weigher = FixedWeightBounds; type UniversalLocation = UniversalLocation; type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; type Currency = Balances; type CurrencyMatcher = (); type TrustedLockers = TrustedLockers; type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; type MaxRemoteLockConsumers = ConstU32<0>; type RemoteLockConsumerIdentifier = (); type WeightInfo = pallet_xcm::TestWeightInfo; type AdminOrigin = EnsureRoot; } type Block = frame_system::mocking::MockBlock; construct_runtime!( pub enum Runtime { System: frame_system, Balances: pallet_balances, MsgQueue: mock_msg_queue, PolkadotXcm: pallet_xcm, ForeignUniques: pallet_uniques, } );