Skip to content

Commit

Permalink
asset-hubs: transfer assets via bridge using pallet-xcm
Browse files Browse the repository at this point in the history
Add xcm routing configuration for bridging between
	Polkadot Asset Hub <-> Kusama Asset Hub.

We explicitly allow only reserve based transfer for KSM/DOT to
cross-consensus AssetHubs and nothing else at first (start defensively).
We can later allow whatever (ETH, TrustBackedAssets, ForeignAssets,
PoolAssets ...).

Add tests for simulation:
- `limited_reserve_transfer_assets` with `pallet_xcm` both ways
- handling `ReserveAssetDeposisted` on both sides

Add local zomienet run for:
- reserve based transfer of KSMs from AHK to AHP
- reserve based transfer of DOTs from AHP to AHK
(check parachains/runtimes/bridge-hubs/README.md in this PR)

Signed-off-by: Branislav Kontur <bkontur@gmail.com>
Signed-off-by: Adrian Catangiu <adrian@parity.io>
  • Loading branch information
acatangiu committed Aug 30, 2023
1 parent bfb241d commit 10ffb9f
Show file tree
Hide file tree
Showing 25 changed files with 2,054 additions and 90 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

140 changes: 140 additions & 0 deletions cumulus/parachains/common/src/xcm_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ use core::marker::PhantomData;
use frame_support::{
traits::{fungibles::Inspect, tokens::ConversionToAssetBalance, ContainsPair},
weights::Weight,
DefaultNoBound,
};
use log;
use sp_runtime::traits::Get;
use xcm::latest::prelude::*;
use xcm_builder::ExporterFor;

/// A `ChargeFeeInFungibles` implementation that converts the output of
/// a given WeightToFee implementation an amount charged in
Expand Down Expand Up @@ -78,3 +80,141 @@ impl<Location: Get<MultiLocation>> ContainsPair<MultiAsset, MultiLocation>
matches!(asset.id, Concrete(ref id) if id == origin && origin == &Location::get())
}
}

/// Trait for matching `Location`.
pub trait MatchesLocation<Location> {
fn matches(&self, location: &Location) -> bool;
}

/// Simple `MultiLocation` filter utility.
#[derive(Debug, DefaultNoBound)]
pub struct LocationFilter<Location> {
/// Requested location equals to `Location`.
pub equals_any: sp_std::vec::Vec<Location>,
/// Requested location starts with `Location`.
pub starts_with_any: sp_std::vec::Vec<Location>,
}

impl<Location> LocationFilter<Location> {
pub fn add_equals(mut self, filter: Location) -> Self {
self.equals_any.push(filter);
self
}
pub fn add_starts_with(mut self, filter: Location) -> Self {
self.starts_with_any.push(filter);
self
}
}

/// `MatchesLocation` implementation which works with `MultiLocation`.
impl MatchesLocation<MultiLocation> for LocationFilter<MultiLocation> {
fn matches(&self, location: &MultiLocation) -> bool {
for filter in &self.equals_any {
if location.eq(filter) {
return true
}
}
for filter in &self.starts_with_any {
if location.starts_with(filter) {
return true
}
}
false
}
}

/// `MatchesLocation` implementation which works with `InteriorMultiLocation`.
impl MatchesLocation<InteriorMultiLocation> for LocationFilter<InteriorMultiLocation> {
fn matches(&self, location: &InteriorMultiLocation) -> bool {
for filter in &self.equals_any {
if location.eq(filter) {
return true
}
}
for filter in &self.starts_with_any {
if location.starts_with(filter) {
return true
}
}
false
}
}

/// `FilteredNetworkExportTable` is adapter for `ExporterFor` implementation
/// which tries to find (`bridge_location`, `fees`) for requested `network` and `remote_location`.
///
/// Inspired by `xcm_builder::NetworkExportTable`:
/// the main difference is that `NetworkExportTable` does not check `remote_location`.
pub struct FilteredNetworkExportTable<T>(sp_std::marker::PhantomData<T>);
impl<
T: Get<
sp_std::vec::Vec<(
NetworkId,
LocationFilter<InteriorMultiLocation>,
MultiLocation,
Option<MultiAsset>,
)>,
>,
> ExporterFor for FilteredNetworkExportTable<T>
{
fn exporter_for(
network: &NetworkId,
remote_location: &InteriorMultiLocation,
_: &Xcm<()>,
) -> Option<(MultiLocation, Option<MultiAsset>)> {
T::get()
.into_iter()
.find(|(ref j, location_filter, ..)| {
j == network && location_filter.matches(remote_location)
})
.map(|(_, _, bridge_location, p)| (bridge_location, p))
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn filtered_network_export_table_works() {
frame_support::parameter_types! {
pub BridgeLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(1234)));

pub BridgeTable: sp_std::vec::Vec<(NetworkId, LocationFilter<InteriorMultiLocation>, MultiLocation, Option<MultiAsset>)> = sp_std::vec![
(
Kusama,
LocationFilter::default()
.add_equals(X1(Parachain(2000)))
.add_equals(X1(Parachain(3000)))
.add_equals(X1(Parachain(4000))),
BridgeLocation::get(),
None
)
];
}

let test_data = vec![
(Polkadot, X1(Parachain(1000)), None),
(Polkadot, X1(Parachain(2000)), None),
(Polkadot, X1(Parachain(3000)), None),
(Polkadot, X1(Parachain(4000)), None),
(Polkadot, X1(Parachain(5000)), None),
(Kusama, X1(Parachain(1000)), None),
(Kusama, X1(Parachain(2000)), Some((BridgeLocation::get(), None))),
(Kusama, X1(Parachain(3000)), Some((BridgeLocation::get(), None))),
(Kusama, X1(Parachain(4000)), Some((BridgeLocation::get(), None))),
(Kusama, X1(Parachain(5000)), None),
];

for (network, remote_location, expected_result) in test_data {
assert_eq!(
FilteredNetworkExportTable::<BridgeTable>::exporter_for(
&network,
&remote_location,
&Xcm::default()
),
expected_result,
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1279,7 +1279,10 @@ impl_runtime_apis! {
}

fn universal_alias() -> Result<(MultiLocation, Junction), BenchmarkError> {
Err(BenchmarkError::Skip)
match xcm_config::bridging::BridgingBenchmarksHelper::prepare_universal_alias() {
Some(alias) => Ok(alias),
None => Err(BenchmarkError::Skip)
}
}

fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ impl<Call> XcmWeightInfo<Call> for AssetHubKusamaXcmWeight<Call> {
XcmGeneric::<Runtime>::clear_transact_status()
}
fn universal_origin(_: &Junction) -> Weight {
Weight::MAX
XcmGeneric::<Runtime>::universal_origin()
}
fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight {
Weight::MAX
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,16 @@ impl<T: frame_system::Config> WeightInfo<T> {
// Minimum execution time: 2_886_000 picoseconds.
Weight::from_parts(3_015_000, 0)
}
// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
pub fn universal_origin() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `1489`
// Minimum execution time: 5_088_000 picoseconds.
Weight::from_parts(5_253_000, 1489)
.saturating_add(T::DbWeight::get().reads(1))
}
pub fn set_fees_mode() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
Expand Down
Loading

0 comments on commit 10ffb9f

Please sign in to comment.