[xcm] Small enhancements for NetworkExportTable and xcm-builder (#1848)

## Summary

This PR introduces several enhancements.

The current implementation of `NetworkExportTable` lacks remote location
filtering support beyond `NetworkId` lookup. To provide more control and
granularity, it's essential to allow configuration for bridging to
different consensus `NetworkId` while restricting access e.g. to
particular remote parachains.

Additionally, the `StartsWith` and `Equals` and
`StartsWithExplicitGlobalConsensus` helper functions, which are in
active use, are moved to the `xcm-builder` and `frame_support` modules
for better code organization.

Adds a new `LocationWithAssetFilters` filter to enable location-based
and asset-related filtering. This filter is useful for configuring the
`pallet_xcm` filter for
[XcmTeleportFilter](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/xcm/pallet-xcm/src/lib.rs#L212)
and
[XcmReserveTransferFilter](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/xcm/pallet-xcm/src/lib.rs#L216)
to restrict specific assets.

Furthermore, the `BridgeMessage` fields are not accessible outside of
`xcm-builder`, limiting the ability to create custom logic dependent on
it.

---------

Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
This commit is contained in:
Branislav Kontur
2023-10-17 11:11:03 +02:00
committed by GitHub
parent e10de2e283
commit 5cdd819ed2
17 changed files with 412 additions and 74 deletions
@@ -27,7 +27,7 @@ use sp_runtime::{
BuildStorage,
};
use xcm::prelude::*;
use xcm_builder::NetworkExportTable;
use xcm_builder::{NetworkExportTable, NetworkExportTableItem};
pub type AccountId = u64;
type Block = frame_system::mocking::MockBlock<TestRuntime>;
@@ -53,8 +53,15 @@ parameter_types! {
pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(ThisNetworkId::get()), Parachain(1000));
pub SiblingBridgeHubLocation: MultiLocation = ParentThen(X1(Parachain(1002))).into();
pub BridgeFeeAsset: AssetId = MultiLocation::parent().into();
pub BridgeTable: Vec<(NetworkId, MultiLocation, Option<MultiAsset>)>
= vec![(BridgedNetworkId::get(), SiblingBridgeHubLocation::get(), Some((BridgeFeeAsset::get(), BASE_FEE).into()))];
pub BridgeTable: Vec<NetworkExportTableItem>
= vec![
NetworkExportTableItem::new(
BridgedNetworkId::get(),
None,
SiblingBridgeHubLocation::get(),
Some((BridgeFeeAsset::get(), BASE_FEE).into())
)
];
}
impl frame_system::Config for TestRuntime {
@@ -21,9 +21,7 @@ use super::{
use crate::ForeignAssets;
use assets_common::{
local_and_foreign_assets::MatchesLocalAndForeignAssetsMultiLocation,
matching::{
FromSiblingParachain, IsForeignConcreteAsset, StartsWith, StartsWithExplicitGlobalConsensus,
},
matching::{FromSiblingParachain, IsForeignConcreteAsset},
};
use frame_support::{
match_types, parameter_types,
@@ -45,8 +43,8 @@ use xcm_builder::{
EnsureXcmOrigin, FungiblesAdapter, HashedDescription, IsConcrete, LocalMint, NoChecking,
ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative,
SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents,
WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit,
TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
};
use xcm_executor::{traits::WithOriginFilter, XcmExecutor};
@@ -18,9 +18,7 @@ use super::{
ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin,
TrustBackedAssetsInstance, WeightToFee, XcmpQueue,
};
use assets_common::matching::{
FromSiblingParachain, IsForeignConcreteAsset, StartsWith, StartsWithExplicitGlobalConsensus,
};
use assets_common::matching::{FromSiblingParachain, IsForeignConcreteAsset};
use frame_support::{
match_types, parameter_types,
traits::{ConstU32, Contains, Everything, Nothing, PalletInfoAccess},
@@ -41,8 +39,8 @@ use xcm_builder::{
EnsureXcmOrigin, FungiblesAdapter, HashedDescription, IsConcrete, LocalMint, NoChecking,
ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative,
SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents,
WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit,
TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
};
use xcm_executor::{traits::WithOriginFilter, XcmExecutor};
@@ -21,9 +21,7 @@ use super::{
use crate::ForeignAssets;
use assets_common::{
local_and_foreign_assets::MatchesLocalAndForeignAssetsMultiLocation,
matching::{
FromSiblingParachain, IsForeignConcreteAsset, StartsWith, StartsWithExplicitGlobalConsensus,
},
matching::{FromSiblingParachain, IsForeignConcreteAsset},
};
use frame_support::{
match_types, parameter_types,
@@ -45,8 +43,8 @@ use xcm_builder::{
EnsureXcmOrigin, FungiblesAdapter, HashedDescription, IsConcrete, LocalMint, NoChecking,
ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative,
SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents,
WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit,
TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
};
use xcm_executor::{traits::WithOriginFilter, XcmExecutor};
@@ -21,11 +21,11 @@ pub mod local_and_foreign_assets;
pub mod matching;
pub mod runtime_api;
use crate::matching::{Equals, LocalMultiLocationPattern, ParentLocation, StartsWith};
use frame_support::traits::EverythingBut;
use crate::matching::{LocalMultiLocationPattern, ParentLocation};
use frame_support::traits::{Equals, EverythingBut};
use parachains_common::AssetIdForTrustBackedAssets;
use xcm::prelude::MultiLocation;
use xcm_builder::{AsPrefixedGeneralIndex, MatchedConvertedConcreteId};
use xcm_builder::{AsPrefixedGeneralIndex, MatchedConvertedConcreteId, StartsWith};
use xcm_executor::traits::{Identity, JustTry};
/// `MultiLocation` vs `AssetIdForTrustBackedAssets` converter for `TrustBackedAssets`
@@ -96,9 +96,9 @@ pub type PoolAssetsConvertedConcreteId<PoolAssetsPalletLocation, Balance> =
#[cfg(test)]
mod tests {
use super::*;
use crate::matching::StartsWithExplicitGlobalConsensus;
use sp_runtime::traits::MaybeEquivalence;
use xcm::latest::prelude::*;
use xcm_builder::StartsWithExplicitGlobalConsensus;
use xcm_executor::traits::{Error as MatchError, MatchesFungibles};
#[test]
@@ -407,13 +407,14 @@ where
#[cfg(test)]
mod tests {
use crate::{
local_and_foreign_assets::MultiLocationConverter, matching::StartsWith,
AssetIdForPoolAssetsConvert, AssetIdForTrustBackedAssetsConvert,
local_and_foreign_assets::MultiLocationConverter, AssetIdForPoolAssetsConvert,
AssetIdForTrustBackedAssetsConvert,
};
use frame_support::traits::EverythingBut;
use pallet_asset_conversion::{MultiAssetIdConversionResult, MultiAssetIdConverter};
use sp_runtime::traits::MaybeEquivalence;
use xcm::latest::prelude::*;
use xcm_builder::StartsWith;
#[test]
fn test_multi_location_converter_works() {
@@ -14,38 +14,12 @@
// limitations under the License.
use cumulus_primitives_core::ParaId;
use frame_support::{
pallet_prelude::Get,
traits::{Contains, ContainsPair},
};
use frame_support::{pallet_prelude::Get, traits::ContainsPair};
use xcm::{
latest::prelude::{MultiAsset, MultiLocation},
prelude::*,
};
pub struct StartsWith<T>(sp_std::marker::PhantomData<T>);
impl<Location: Get<MultiLocation>> Contains<MultiLocation> for StartsWith<Location> {
fn contains(t: &MultiLocation) -> bool {
t.starts_with(&Location::get())
}
}
pub struct Equals<T>(sp_std::marker::PhantomData<T>);
impl<Location: Get<MultiLocation>> Contains<MultiLocation> for Equals<Location> {
fn contains(t: &MultiLocation) -> bool {
t == &Location::get()
}
}
pub struct StartsWithExplicitGlobalConsensus<T>(sp_std::marker::PhantomData<T>);
impl<Network: Get<NetworkId>> Contains<MultiLocation>
for StartsWithExplicitGlobalConsensus<Network>
{
fn contains(t: &MultiLocation) -> bool {
matches!(t.interior.global_consensus(), Ok(requested_network) if requested_network.eq(&Network::get()))
}
}
frame_support::parameter_types! {
pub LocalMultiLocationPattern: MultiLocation = MultiLocation::new(0, Here);
pub ParentLocation: MultiLocation = MultiLocation::parent();
@@ -14,11 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Various implementations of `ContainsPair<MultiAsset, MultiLocation>`.
//! Various implementations of `ContainsPair<MultiAsset, MultiLocation>` or
//! `Contains<(MultiLocation, Vec<MultiAsset>)>`.
use frame_support::traits::{ContainsPair, Get};
use sp_std::marker::PhantomData;
use xcm::latest::{AssetId::Concrete, MultiAsset, MultiAssetFilter, MultiLocation};
use frame_support::traits::{Contains, ContainsPair, Get};
use sp_std::{marker::PhantomData, vec::Vec};
use xcm::latest::{AssetId::Concrete, MultiAsset, MultiAssetFilter, MultiLocation, WildMultiAsset};
/// Accepts an asset iff it is a native asset.
pub struct NativeAsset;
@@ -40,3 +41,171 @@ impl<T: Get<(MultiAssetFilter, MultiLocation)>> ContainsPair<MultiAsset, MultiLo
a.matches(asset) && &o == origin
}
}
/// Accepts a tuple `(location, assets)` if the `location` is contained in the `Contains`
/// implementation of the given `Location` and if every asset from `assets` matches at least one of
/// the `MultiAssetFilter` instances provided by the `Get` implementation of `AssetFilters`.
pub struct LocationWithAssetFilters<Location, AssetFilters>(
sp_std::marker::PhantomData<(Location, AssetFilters)>,
);
impl<Location: Contains<MultiLocation>, AssetFilters: Get<Vec<MultiAssetFilter>>>
Contains<(MultiLocation, Vec<MultiAsset>)> for LocationWithAssetFilters<Location, AssetFilters>
{
fn contains((location, assets): &(MultiLocation, Vec<MultiAsset>)) -> bool {
log::trace!(target: "xcm::contains", "LocationWithAssetFilters location: {:?}, assets: {:?}", location, assets);
// `location` must match the `Location` filter.
if !Location::contains(location) {
return false
}
// All `assets` must match at least one of the `AssetFilters`.
let filters = AssetFilters::get();
assets.iter().all(|asset| {
for filter in &filters {
if filter.matches(asset) {
return true
}
}
false
})
}
}
/// Implementation of `Get<Vec<MultiAssetFilter>>` which accepts every asset.
/// (For example, it can be used with `LocationWithAssetFilters`).
pub struct AllAssets;
impl Get<Vec<MultiAssetFilter>> for AllAssets {
fn get() -> Vec<MultiAssetFilter> {
sp_std::vec![MultiAssetFilter::Wild(WildMultiAsset::All)]
}
}
#[cfg(test)]
mod tests {
use super::*;
use frame_support::traits::Equals;
use xcm::latest::prelude::*;
#[test]
fn location_with_asset_filters_works() {
frame_support::parameter_types! {
pub ParaA: MultiLocation = MultiLocation::new(1, X1(Parachain(1001)));
pub ParaB: MultiLocation = MultiLocation::new(1, X1(Parachain(1002)));
pub ParaC: MultiLocation = MultiLocation::new(1, X1(Parachain(1003)));
pub AssetXLocation: MultiLocation = MultiLocation::new(1, X1(GeneralIndex(1111)));
pub AssetYLocation: MultiLocation = MultiLocation::new(1, X1(GeneralIndex(2222)));
pub AssetZLocation: MultiLocation = MultiLocation::new(1, X1(GeneralIndex(3333)));
pub OnlyAssetXOrAssetY: sp_std::vec::Vec<MultiAssetFilter> = sp_std::vec![
Wild(AllOf { fun: WildFungible, id: Concrete(AssetXLocation::get()) }),
Wild(AllOf { fun: WildFungible, id: Concrete(AssetYLocation::get()) }),
];
pub OnlyAssetZ: sp_std::vec::Vec<MultiAssetFilter> = sp_std::vec![
Wild(AllOf { fun: WildFungible, id: Concrete(AssetZLocation::get()) })
];
}
let test_data: Vec<(MultiLocation, Vec<MultiAsset>, bool)> = vec![
(ParaA::get(), vec![(AssetXLocation::get(), 1).into()], true),
(ParaA::get(), vec![(AssetYLocation::get(), 1).into()], true),
(ParaA::get(), vec![(AssetZLocation::get(), 1).into()], false),
(
ParaA::get(),
vec![(AssetXLocation::get(), 1).into(), (AssetYLocation::get(), 1).into()],
true,
),
(
ParaA::get(),
vec![(AssetXLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
false,
),
(
ParaA::get(),
vec![(AssetYLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
false,
),
(
ParaA::get(),
vec![
(AssetXLocation::get(), 1).into(),
(AssetYLocation::get(), 1).into(),
(AssetZLocation::get(), 1).into(),
],
false,
),
(ParaB::get(), vec![(AssetXLocation::get(), 1).into()], false),
(ParaB::get(), vec![(AssetYLocation::get(), 1).into()], false),
(ParaB::get(), vec![(AssetZLocation::get(), 1).into()], true),
(
ParaB::get(),
vec![(AssetXLocation::get(), 1).into(), (AssetYLocation::get(), 1).into()],
false,
),
(
ParaB::get(),
vec![(AssetXLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
false,
),
(
ParaB::get(),
vec![(AssetYLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
false,
),
(
ParaB::get(),
vec![
(AssetXLocation::get(), 1).into(),
(AssetYLocation::get(), 1).into(),
(AssetZLocation::get(), 1).into(),
],
false,
),
(ParaC::get(), vec![(AssetXLocation::get(), 1).into()], true),
(ParaC::get(), vec![(AssetYLocation::get(), 1).into()], true),
(ParaC::get(), vec![(AssetZLocation::get(), 1).into()], true),
(
ParaC::get(),
vec![(AssetXLocation::get(), 1).into(), (AssetYLocation::get(), 1).into()],
true,
),
(
ParaC::get(),
vec![(AssetXLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
true,
),
(
ParaC::get(),
vec![(AssetYLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
true,
),
(
ParaC::get(),
vec![
(AssetXLocation::get(), 1).into(),
(AssetYLocation::get(), 1).into(),
(AssetZLocation::get(), 1).into(),
],
true,
),
];
type Filter = (
// For ParaA accept only asset X and Y.
LocationWithAssetFilters<Equals<ParaA>, OnlyAssetXOrAssetY>,
// For ParaB accept only asset Z.
LocationWithAssetFilters<Equals<ParaB>, OnlyAssetZ>,
// For ParaC accept all assets.
LocationWithAssetFilters<Equals<ParaC>, AllAssets>,
);
for (location, assets, expected_result) in test_data {
assert_eq!(
Filter::contains(&(location, assets.clone())),
expected_result,
"expected_result: {expected_result} not matched for (location, assets): ({:?}, {:?})!", location, assets,
)
}
}
}
+5 -2
View File
@@ -83,6 +83,9 @@ pub use weight::{
FixedRateOfFungible, FixedWeightBounds, TakeRevenue, UsingComponents, WeightInfoBounds,
};
mod matches_location;
pub use matches_location::{StartsWith, StartsWithExplicitGlobalConsensus};
mod matches_token;
pub use matches_token::{IsAbstract, IsConcrete};
@@ -90,7 +93,7 @@ mod matcher;
pub use matcher::{CreateMatcher, MatchXcm, Matcher};
mod filter_asset_location;
pub use filter_asset_location::{Case, NativeAsset};
pub use filter_asset_location::{AllAssets, Case, LocationWithAssetFilters, NativeAsset};
mod routing;
pub use routing::{WithTopicSource, WithUniqueTopic};
@@ -99,7 +102,7 @@ mod universal_exports;
pub use universal_exports::{
ensure_is_remote, BridgeBlobDispatcher, BridgeMessage, DispatchBlob, DispatchBlobError,
ExporterFor, HaulBlob, HaulBlobError, HaulBlobExporter, NetworkExportTable,
SovereignPaidRemoteExporter, UnpaidLocalExporter, UnpaidRemoteExporter,
NetworkExportTableItem, SovereignPaidRemoteExporter, UnpaidLocalExporter, UnpaidRemoteExporter,
};
mod origin_aliases;
@@ -0,0 +1,50 @@
// 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 <http://www.gnu.org/licenses/>.
//! Various implementations and utilities for matching and filtering `MultiLocation` and
//! `InteriorMultiLocation` types.
use frame_support::traits::{Contains, Get};
use xcm::latest::{InteriorMultiLocation, MultiLocation, NetworkId};
/// An implementation of `Contains` that checks for `MultiLocation` or
/// `InteriorMultiLocation` if starts with the provided type `T`.
pub struct StartsWith<T>(sp_std::marker::PhantomData<T>);
impl<T: Get<MultiLocation>> Contains<MultiLocation> for StartsWith<T> {
fn contains(t: &MultiLocation) -> bool {
t.starts_with(&T::get())
}
}
impl<T: Get<InteriorMultiLocation>> Contains<InteriorMultiLocation> for StartsWith<T> {
fn contains(t: &InteriorMultiLocation) -> bool {
t.starts_with(&T::get())
}
}
/// An implementation of `Contains` that checks for `MultiLocation` or
/// `InteriorMultiLocation` if starts with expected `GlobalConsensus(NetworkId)` provided as type
/// `T`.
pub struct StartsWithExplicitGlobalConsensus<T>(sp_std::marker::PhantomData<T>);
impl<T: Get<NetworkId>> Contains<MultiLocation> for StartsWithExplicitGlobalConsensus<T> {
fn contains(location: &MultiLocation) -> bool {
matches!(location.interior.global_consensus(), Ok(requested_network) if requested_network.eq(&T::get()))
}
}
impl<T: Get<NetworkId>> Contains<InteriorMultiLocation> for StartsWithExplicitGlobalConsensus<T> {
fn contains(location: &InteriorMultiLocation) -> bool {
matches!(location.global_consensus(), Ok(requested_network) if requested_network.eq(&T::get()))
}
}
@@ -27,8 +27,14 @@ parameter_types! {
pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(100));
pub RelayUniversalLocation: Junctions = X1(GlobalConsensus(Local::get()));
pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get()));
pub BridgeTable: Vec<(NetworkId, MultiLocation, Option<MultiAsset>)>
= vec![(Remote::get(), MultiLocation::parent(), Some((Parent, 200u128 + if UsingTopic::get() { 20 } else { 0 }).into()))];
pub BridgeTable: Vec<NetworkExportTableItem> = vec![
NetworkExportTableItem::new(
Remote::get(),
None,
MultiLocation::parent(),
Some((Parent, 200u128 + if UsingTopic::get() { 20 } else { 0 }).into())
)
];
// ^^^ 100 to use the bridge (export) and 100 for the remote execution weight (5 instructions
// x (10 + 10) weight each).
}
@@ -24,8 +24,14 @@ parameter_types! {
pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1000));
pub ParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1));
pub RemoteParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Remote::get()), Parachain(1));
pub BridgeTable: Vec<(NetworkId, MultiLocation, Option<MultiAsset>)>
= vec![(Remote::get(), (Parent, Parachain(1)).into(), None)];
pub BridgeTable: Vec<NetworkExportTableItem> = vec![
NetworkExportTableItem::new(
Remote::get(),
None,
(Parent, Parachain(1)).into(),
None
)
];
}
type TheBridge = TestBridge<
BridgeBlobDispatcher<TestRemoteIncomingRouter, RemoteParaBridgeUniversalLocation, ()>,
@@ -24,8 +24,14 @@ parameter_types! {
pub UniversalLocation: Junctions = X1(GlobalConsensus(Local::get()));
pub ParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1));
pub RemoteParaBridgeUniversalLocation: Junctions = X2(GlobalConsensus(Remote::get()), Parachain(1));
pub BridgeTable: Vec<(NetworkId, MultiLocation, Option<MultiAsset>)>
= vec![(Remote::get(), Parachain(1).into(), None)];
pub BridgeTable: Vec<NetworkExportTableItem> = vec![
NetworkExportTableItem::new(
Remote::get(),
None,
Parachain(1).into(),
None
)
];
}
type TheBridge = TestBridge<
BridgeBlobDispatcher<TestRemoteIncomingRouter, RemoteParaBridgeUniversalLocation, ()>,
@@ -24,8 +24,14 @@ parameter_types! {
pub UniversalLocation: Junctions = X2(GlobalConsensus(Local::get()), Parachain(1000));
pub RelayUniversalLocation: Junctions = X1(GlobalConsensus(Local::get()));
pub RemoteUniversalLocation: Junctions = X1(GlobalConsensus(Remote::get()));
pub BridgeTable: Vec<(NetworkId, MultiLocation, Option<MultiAsset>)>
= vec![(Remote::get(), MultiLocation::parent(), None)];
pub BridgeTable: Vec<NetworkExportTableItem> = vec![
NetworkExportTableItem::new(
Remote::get(),
None,
MultiLocation::parent(),
None
)
];
}
type TheBridge =
TestBridge<BridgeBlobDispatcher<TestRemoteIncomingRouter, RemoteUniversalLocation, ()>>;
@@ -117,16 +117,54 @@ impl ExporterFor for Tuple {
}
}
/// Configuration item representing a single exporter in the `NetworkExportTable`.
pub struct NetworkExportTableItem {
/// Supported remote network.
pub remote_network: NetworkId,
/// Remote location filter.
/// If `Some`, the requested remote location must be equal to one of the items in the vector.
/// These are locations in the remote network.
/// If `None`, then the check is skipped.
pub remote_location_filter: Option<Vec<InteriorMultiLocation>>,
/// Locally-routable bridge with bridging capabilities to the `remote_network` and
/// `remote_location`. See [`ExporterFor`] for more details.
pub bridge: MultiLocation,
/// The local payment.
/// See [`ExporterFor`] for more details.
pub payment: Option<MultiAsset>,
}
impl NetworkExportTableItem {
pub fn new(
remote_network: NetworkId,
remote_location_filter: Option<Vec<InteriorMultiLocation>>,
bridge: MultiLocation,
payment: Option<MultiAsset>,
) -> Self {
Self { remote_network, remote_location_filter, bridge, payment }
}
}
/// An adapter for the implementation of `ExporterFor`, which attempts to find the
/// `(bridge_location, payment)` for the requested `network` and `remote_location` in the provided
/// `T` table containing various exporters.
pub struct NetworkExportTable<T>(sp_std::marker::PhantomData<T>);
impl<T: Get<Vec<(NetworkId, MultiLocation, Option<MultiAsset>)>>> ExporterFor
for NetworkExportTable<T>
{
impl<T: Get<Vec<NetworkExportTableItem>>> ExporterFor for NetworkExportTable<T> {
fn exporter_for(
network: &NetworkId,
_: &InteriorMultiLocation,
remote_location: &InteriorMultiLocation,
_: &Xcm<()>,
) -> Option<(MultiLocation, Option<MultiAsset>)> {
T::get().into_iter().find(|(ref j, ..)| j == network).map(|(_, l, p)| (l, p))
T::get()
.into_iter()
.find(|item| {
&item.remote_network == network &&
item.remote_location_filter
.as_ref()
.map(|filters| filters.iter().any(|filter| filter == remote_location))
.unwrap_or(true)
})
.map(|item| (item.bridge, item.payment))
}
}
@@ -329,8 +367,8 @@ pub struct BridgeMessage {
/// The message destination as a *Universal Location*. This means it begins with a
/// `GlobalConsensus` junction describing the network under which global consensus happens.
/// If this does not match our global consensus then it's a fatal error.
universal_dest: VersionedInteriorMultiLocation,
message: VersionedXcm<()>,
pub universal_dest: VersionedInteriorMultiLocation,
pub message: VersionedXcm<()>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -504,7 +542,7 @@ mod tests {
pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(Local::get()), Parachain(1234));
pub DifferentRemote: NetworkId = ByGenesis([22; 32]);
// no routers
pub BridgeTable: Vec<(NetworkId, MultiLocation, Option<MultiAsset>)> = vec![];
pub BridgeTable: Vec<NetworkExportTableItem> = vec![];
}
// check with local destination (should be remote)
@@ -539,4 +577,74 @@ mod tests {
>,
>(remote_dest, |result| assert_eq!(Err(NotApplicable), result));
}
#[test]
fn network_export_table_works() {
frame_support::parameter_types! {
pub NetworkA: NetworkId = ByGenesis([0; 32]);
pub Parachain1000InNetworkA: InteriorMultiLocation = X1(Parachain(1000));
pub Parachain2000InNetworkA: InteriorMultiLocation = X1(Parachain(2000));
pub NetworkB: NetworkId = ByGenesis([1; 32]);
pub BridgeToALocation: MultiLocation = MultiLocation::new(1, X1(Parachain(1234)));
pub BridgeToBLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(4321)));
pub PaymentForNetworkAAndParachain2000: MultiAsset = (MultiLocation::parent(), 150).into();
pub BridgeTable: sp_std::vec::Vec<NetworkExportTableItem> = sp_std::vec![
// NetworkA allows `Parachain(1000)` as remote location WITHOUT payment.
NetworkExportTableItem::new(
NetworkA::get(),
Some(vec![Parachain1000InNetworkA::get()]),
BridgeToALocation::get(),
None
),
// NetworkA allows `Parachain(2000)` as remote location WITH payment.
NetworkExportTableItem::new(
NetworkA::get(),
Some(vec![Parachain2000InNetworkA::get()]),
BridgeToALocation::get(),
Some(PaymentForNetworkAAndParachain2000::get())
),
// NetworkB allows all remote location.
NetworkExportTableItem::new(
NetworkB::get(),
None,
BridgeToBLocation::get(),
None
)
];
}
let test_data = vec![
(NetworkA::get(), X1(Parachain(1000)), Some((BridgeToALocation::get(), None))),
(NetworkA::get(), X2(Parachain(1000), GeneralIndex(1)), None),
(
NetworkA::get(),
X1(Parachain(2000)),
Some((BridgeToALocation::get(), Some(PaymentForNetworkAAndParachain2000::get()))),
),
(NetworkA::get(), X2(Parachain(2000), GeneralIndex(1)), None),
(NetworkA::get(), X1(Parachain(3000)), None),
(NetworkB::get(), X1(Parachain(1000)), Some((BridgeToBLocation::get(), None))),
(NetworkB::get(), X1(Parachain(2000)), Some((BridgeToBLocation::get(), None))),
(NetworkB::get(), X1(Parachain(3000)), Some((BridgeToBLocation::get(), None))),
];
for (network, remote_location, expected_result) in test_data {
assert_eq!(
NetworkExportTable::<BridgeTable>::exporter_for(
&network,
&remote_location,
&Xcm::default()
),
expected_result,
"expected_result: {:?} not matched for network: {:?} and remote_location: {:?}",
expected_result,
network,
remote_location,
)
}
}
}
+1 -1
View File
@@ -34,7 +34,7 @@ mod members;
#[allow(deprecated)]
pub use members::{AllowAll, DenyAll, Filter};
pub use members::{
AsContains, ChangeMembers, Contains, ContainsLengthBound, ContainsPair, Everything,
AsContains, ChangeMembers, Contains, ContainsLengthBound, ContainsPair, Equals, Everything,
EverythingBut, FromContainsPair, InitializeMembers, InsideBoth, IsInVec, Nothing,
RankedMembers, SortedMembers, TheseExcept,
};
@@ -137,6 +137,14 @@ impl<A, B, These: ContainsPair<A, B>, Those: ContainsPair<A, B>> ContainsPair<A,
}
}
/// An implementation of [`Contains`] which contains only equal members to `T`.
pub struct Equals<T>(PhantomData<T>);
impl<X: PartialEq, T: super::Get<X>> Contains<X> for Equals<T> {
fn contains(t: &X) -> bool {
t == &T::get()
}
}
/// Create a type which implements the `Contains` trait for a particular type with syntax similar
/// to `matches!`.
#[macro_export]