[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
@@ -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,
)
}
}
}