// Copyright 2019-2022 Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus 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. // Cumulus 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 Cumulus. If not, see . //! Holds the XCM specific configuration that would otherwise be in lib.rs //! //! This configuration dictates how the Penpal chain will communicate with other chains. //! //! One of the main uses of the penpal chain will be to be a benefactor of reserve asset transfers //! with statemine as the reserve. At present no derivative tokens are minted on receipt of a //! ReserveAssetTransferDeposited message but that will but the intension will be to support this soon. use super::{ AccountId, AssetId as AssetIdPalletAssets, Assets, Balance, Balances, Call, Event, Origin, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, WeightToFee, XcmpQueue, }; use core::marker::PhantomData; use frame_support::{ log, match_types, parameter_types, traits::{ fungibles::{self, Balanced, CreditOf}, Contains, Everything, Get, Nothing, }, weights::Weight, }; use pallet_asset_tx_payment::HandleCredit; use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use polkadot_runtime_common::impls::ToAuthor; use sp_runtime::traits::Zero; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteAssetId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{ traits::{FilterAssetLocation, JustTry, ShouldExecute}, XcmExecutor, }; parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); pub const RelayNetwork: NetworkId = NetworkId::Any; pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used /// when determining ownership of accounts for asset transacting and when attempting to use XCM /// `Transact` in order to determine the dispatch Origin. pub type LocationToAccountId = ( // The parent (Relay-chain) origin converts to the parent `AccountId`. ParentIsPreset, // Sibling parachain origins convert to AccountId via the `ParaId::into`. SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, ); /// Means for transacting assets on this chain. pub type CurrencyTransactor = CurrencyAdapter< // Use this currency: Balances, // Use this currency when it is a fungible asset matching the given location or name: IsConcrete, // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID: LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, // We don't track any teleports. (), >; /// Means for transacting assets besides the native currency on this chain. pub type FungiblesTransactor = FungiblesAdapter< // Use this fungibles implementation: Assets, // Use this currency when it is a fungible asset matching the given location or name: ConvertedConcreteAssetId< AssetIdPalletAssets, Balance, AsPrefixedGeneralIndex, JustTry, >, // Convert an XCM MultiLocation into a local account id: LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, // We only want to allow teleports of known assets. We use non-zero issuance as an indication // that this asset is known. NonZeroIssuance, // The account to use for tracking teleports. CheckingAccount, >; /// Means for transacting assets on this chain. pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor); /// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, /// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can /// biases the kind of local `Origin` it will become. pub type XcmOriginToTransactDispatchOrigin = ( // Sovereign account converter; this attempts to derive an `AccountId` from the origin location // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for // foreign chains who want to have a local sovereign account on this chain which they control. SovereignSignedViaLocation, // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when // recognized. RelayChainAsNative, // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when // recognized. SiblingParachainAsNative, // Native signed account converter; this just converts an `AccountId32` origin into a normal // `Origin::Signed` origin of the same 32-byte value. SignedAccountId32AsNative, // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. XcmPassthrough, ); parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. pub UnitWeightCost: Weight = 1_000_000_000; pub const MaxInstructions: u32 = 100; } match_types! { pub type ParentOrParentsExecutivePlurality: impl Contains = { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Executive, .. }) } }; pub type CommonGoodAssetsParachain: impl Contains = { MultiLocation { parents: 1, interior: X1(Parachain(1000)) } }; } //TODO: move DenyThenTry to polkadot's xcm module. /// Deny executing the xcm message if it matches any of the Deny filter regardless of anything else. /// If it passes the Deny, and matches one of the Allow cases then it is let through. pub struct DenyThenTry(PhantomData, PhantomData) where Deny: ShouldExecute, Allow: ShouldExecute; impl ShouldExecute for DenyThenTry where Deny: ShouldExecute, Allow: ShouldExecute, { fn should_execute( origin: &MultiLocation, message: &mut Xcm, max_weight: Weight, weight_credit: &mut Weight, ) -> Result<(), ()> { Deny::should_execute(origin, message, max_weight, weight_credit)?; Allow::should_execute(origin, message, max_weight, weight_credit) } } // See issue #5233 pub struct DenyReserveTransferToRelayChain; impl ShouldExecute for DenyReserveTransferToRelayChain { fn should_execute( origin: &MultiLocation, message: &mut Xcm, _max_weight: Weight, _weight_credit: &mut Weight, ) -> Result<(), ()> { if message.0.iter().any(|inst| { matches!( inst, InitiateReserveWithdraw { reserve: MultiLocation { parents: 1, interior: Here }, .. } | DepositReserveAsset { dest: MultiLocation { parents: 1, interior: Here }, .. } | TransferReserveAsset { dest: MultiLocation { parents: 1, interior: Here }, .. } ) }) { return Err(()) // Deny } // allow reserve transfers to arrive from relay chain if matches!(origin, MultiLocation { parents: 1, interior: Here }) && message.0.iter().any(|inst| matches!(inst, ReserveAssetDeposited { .. })) { log::warn!( target: "xcm::barriers", "Unexpected ReserveAssetDeposited from the relay chain", ); } // Permit everything else Ok(()) } } pub type Barrier = DenyThenTry< DenyReserveTransferToRelayChain, ( TakeWeightCredit, AllowTopLevelPaidExecutionFrom, // Parent and its exec plurality get free execution AllowUnpaidExecutionFrom, // Assets Common Good parachain gets free execution AllowUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, ), >; /// Type alias to conveniently refer to `frame_system`'s `Config::AccountId`. pub type AccountIdOf = ::AccountId; /// Asset filter that allows all assets from a certain location. pub struct AssetsFrom(PhantomData); impl> FilterAssetLocation for AssetsFrom { fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { let loc = T::get(); &loc == origin && matches!(asset, MultiAsset { id: AssetId::Concrete(asset_loc), fun: Fungible(_a) } if asset_loc.match_and_split(&loc).is_some()) } } /// Allow checking in assets that have issuance > 0. pub struct NonZeroIssuance(PhantomData<(AccountId, Assets)>); impl Contains<>::AssetId> for NonZeroIssuance where Assets: fungibles::Inspect, { fn contains(id: &>::AssetId) -> bool { !Assets::total_issuance(*id).is_zero() } } /// A `HandleCredit` implementation that naively transfers the fees to the block author. /// Will drop and burn the assets in case the transfer fails. pub struct AssetsToBlockAuthor(PhantomData); impl HandleCredit, pallet_assets::Pallet> for AssetsToBlockAuthor where R: pallet_authorship::Config + pallet_assets::Config, AccountIdOf: From + Into, { fn handle_credit(credit: CreditOf, pallet_assets::Pallet>) { if let Some(author) = pallet_authorship::Pallet::::author() { // In case of error: Will drop the result triggering the `OnDrop` of the imbalance. let _ = pallet_assets::Pallet::::resolve(&author, credit); } } } pub trait Reserve { /// Returns assets reserve location. fn reserve(&self) -> Option; } // Takes the chain part of a MultiAsset impl Reserve for MultiAsset { fn reserve(&self) -> Option { if let AssetId::Concrete(location) = self.id.clone() { let first_interior = location.first_interior(); let parents = location.parent_count(); match (parents, first_interior.clone()) { (0, Some(Parachain(id))) => Some(MultiLocation::new(0, X1(Parachain(id.clone())))), (1, Some(Parachain(id))) => Some(MultiLocation::new(1, X1(Parachain(id.clone())))), (1, _) => Some(MultiLocation::parent()), _ => None, } } else { None } } } /// A `FilterAssetLocation` implementation. Filters multi native assets whose /// reserve is same with `origin`. pub struct MultiNativeAsset; impl FilterAssetLocation for MultiNativeAsset { fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { if let Some(ref reserve) = asset.reserve() { if reserve == origin { return true } } false } } parameter_types! { pub CommonGoodAssetsLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(1000))); // ALWAYS ensure that the index in PalletInstance stays up-to-date with // Statemint's Assets pallet index pub CommonGoodAssetsPalletLocation: MultiLocation = MultiLocation::new(1, X2(Parachain(1000), PalletInstance(50))); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); } pub type Reserves = (NativeAsset, AssetsFrom); pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type Call = Call; type XcmSender = XcmRouter; // How to withdraw and deposit an asset. type AssetTransactor = AssetTransactors; type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = MultiNativeAsset; // TODO: maybe needed to be replaced by Reserves type IsTeleporter = NativeAsset; type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = UsingComponents>; type ResponseHandler = PolkadotXcm; type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. pub type LocalOriginToLocation = SignedToAccountId32; /// The means for routing XCM messages which are not for local execution into the right message /// queues. pub type XcmRouter = ( // Two routers - use UMP to communicate with the relay chain: cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); impl pallet_xcm::Config for Runtime { type Event = Event; type SendXcmOrigin = EnsureXcmOrigin; type XcmRouter = XcmRouter; type ExecuteXcmOrigin = EnsureXcmOrigin; type XcmExecuteFilter = Nothing; // ^ Disable dispatchable execute on the XCM pallet. // Needs to be `Everything` for local testing. type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; type Weigher = FixedWeightBounds; type LocationInverter = LocationInverter; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; // ^ Override for AdvertisedXcmVersion default type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; } impl cumulus_pallet_xcm::Config for Runtime { type Event = Event; type XcmExecutor = XcmExecutor; }