diff --git a/Cargo.lock b/Cargo.lock index dc85690c1fb..abe1d27c1c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,6 +420,8 @@ dependencies = [ "pallet-aura", "pallet-authorship", "pallet-balances", + "pallet-bridge-transfer", + "pallet-bridge-transfer-primitives", "pallet-collator-selection", "pallet-multisig", "pallet-nfts", @@ -447,6 +449,7 @@ dependencies = [ "sp-consensus-aura", "sp-core", "sp-inherents", + "sp-io", "sp-offchain", "sp-runtime", "sp-session", @@ -515,6 +518,8 @@ dependencies = [ "pallet-aura", "pallet-authorship", "pallet-balances", + "pallet-bridge-transfer", + "pallet-bridge-transfer-primitives", "pallet-collator-selection", "pallet-multisig", "pallet-nfts", @@ -611,6 +616,8 @@ dependencies = [ "pallet-aura", "pallet-authorship", "pallet-balances", + "pallet-bridge-transfer", + "pallet-bridge-transfer-primitives", "pallet-collator-selection", "pallet-multisig", "pallet-nft-fractionalization", @@ -667,6 +674,8 @@ dependencies = [ "hex-literal 0.3.4", "pallet-assets", "pallet-balances", + "pallet-bridge-transfer", + "pallet-bridge-transfer-primitives", "pallet-collator-selection", "pallet-session", "pallet-xcm", @@ -682,6 +691,7 @@ dependencies = [ "sp-std", "substrate-wasm-builder", "xcm", + "xcm-builder", "xcm-executor", ] @@ -7392,6 +7402,39 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-bridge-transfer" +version = "0.1.0" +dependencies = [ + "cumulus-pallet-xcmp-queue", + "frame-benchmarking", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "log", + "pallet-balances", + "pallet-bridge-transfer-primitives", + "parity-scale-codec", + "polkadot-parachain", + "scale-info", + "sp-runtime", + "sp-std", + "sp-version", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "pallet-bridge-transfer-primitives" +version = "0.1.0" +dependencies = [ + "frame-support", + "sp-std", + "xcm", + "xcm-builder", +] + [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 77b32019681..d5b3b9c2aa9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ members = [ "primitives/utility", "polkadot-parachain", "parachains/common", + "parachains/pallets/bridge-transfer", "parachains/pallets/parachain-info", "parachains/pallets/ping", "parachains/runtimes/testing/rococo-parachain", diff --git a/parachains/pallets/bridge-transfer/Cargo.toml b/parachains/pallets/bridge-transfer/Cargo.toml new file mode 100644 index 00000000000..b4e47e472ba --- /dev/null +++ b/parachains/pallets/bridge-transfer/Cargo.toml @@ -0,0 +1,62 @@ +[package] +name = "pallet-bridge-transfer" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://docs.substrate.io/" +repository = "https://github.com/paritytech/cumulus/" +description = "Pallet for message transfer through bridges" +readme = "README.md" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.3.0", default-features = false, features = ["derive"] } +log = { version = "0.4.14", default-features = false } + +# Substrate +sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", optional = true, default-features = false, branch = "master" } +frame-system-benchmarking = { git = "https://github.com/paritytech/substrate", optional = true, default-features = false, branch = "master" } +frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } + +# Polkadot +xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } + +# Cumulus +pallet-bridge-transfer-primitives = { path = "./primitives", default-features = false } + +[dev-dependencies] +sp-version = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "master" } +cumulus-pallet-xcmp-queue = { path = "../../../pallets/xcmp-queue" } + +[features] +default = ["std"] +std = [ + "codec/std", + "log/std", + "scale-info/std", + "pallet-bridge-transfer-primitives/std", + "sp-std/std", + "sp-runtime/std", + "frame-support/std", + "frame-system/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/parachains/pallets/bridge-transfer/primitives/Cargo.toml b/parachains/pallets/bridge-transfer/primitives/Cargo.toml new file mode 100644 index 00000000000..2ac8cf450e2 --- /dev/null +++ b/parachains/pallets/bridge-transfer/primitives/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "pallet-bridge-transfer-primitives" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://docs.substrate.io/" +repository = "https://github.com/paritytech/cumulus/" +description = "Pallet with primitives for message transfer through bridges" + +[dependencies] + +# Substrate +sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } + +# Polkadot +xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } + +[features] +default = ["std"] +std = [ + "sp-std/std", + "frame-support/std", + "xcm/std", + "xcm-builder/std", +] diff --git a/parachains/pallets/bridge-transfer/primitives/src/asset_filter.rs b/parachains/pallets/bridge-transfer/primitives/src/asset_filter.rs new file mode 100644 index 00000000000..d59cb2a9f26 --- /dev/null +++ b/parachains/pallets/bridge-transfer/primitives/src/asset_filter.rs @@ -0,0 +1,72 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Primitives for matching asset `MultiLocation`. + +use xcm::prelude::*; + +/// Trait for matching asset location +pub trait MatchAssetLocation { + fn matches(&self, location: &MultiLocation) -> bool; +} + +/// Simple asset location filter +#[derive(Debug)] +pub enum AssetFilter { + ByMultiLocation(MultiLocationFilter), +} + +impl MatchAssetLocation for AssetFilter { + fn matches(&self, asset_location: &MultiLocation) -> bool { + match self { + AssetFilter::ByMultiLocation(by_location) => by_location.matches(asset_location), + } + } +} + +#[derive(Debug, Default)] +pub struct MultiLocationFilter { + /// Requested location equals to `MultiLocation` + pub equals_any: sp_std::vec::Vec, + /// Requested location starts with `MultiLocation` + pub starts_with_any: sp_std::vec::Vec, +} + +impl MultiLocationFilter { + pub fn add_equals(mut self, filter: MultiLocation) -> Self { + self.equals_any.push(filter); + self + } + pub fn add_starts_with(mut self, filter: MultiLocation) -> Self { + self.starts_with_any.push(filter); + self + } +} + +impl MatchAssetLocation for MultiLocationFilter { + 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 + } +} diff --git a/parachains/pallets/bridge-transfer/primitives/src/config.rs b/parachains/pallets/bridge-transfer/primitives/src/config.rs new file mode 100644 index 00000000000..eac3bcbc7e6 --- /dev/null +++ b/parachains/pallets/bridge-transfer/primitives/src/config.rs @@ -0,0 +1,121 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Primitives for easier configuration. + +use crate::{ + AssetFilter, EnsureReachableDestination, MaybePaidLocation, ReachableDestination, + ReachableDestinationError, +}; +use frame_support::{ + traits::{ConstU32, Get}, + BoundedBTreeMap, +}; +use xcm::prelude::*; +use xcm_builder::ExporterFor; + +/// Configuration represents bridge to target location with possible asset filtering +#[derive(Debug)] +pub struct BridgeConfig { + /// Location, which is able to bridge XCM messages to bridged network + pub bridge_location: MaybePaidLocation, + /// Contains target destination on bridged network. E.g.: MultiLocation of Statemine/t on different consensus + its configuration + pub allowed_target_locations: sp_std::vec::Vec<(MaybePaidLocation, Option)>, +} + +impl BridgeConfig { + pub fn new(bridge_location: MaybePaidLocation) -> Self { + Self { bridge_location, allowed_target_locations: Default::default() } + } + + pub fn add_target_location( + mut self, + target_location: MaybePaidLocation, + asset_filter: Option, + ) -> Self { + self.allowed_target_locations.push((target_location, asset_filter)); + self + } + + pub fn allowed_target_location_for( + &self, + destination: &MultiLocation, + ) -> Option<(&MaybePaidLocation, &Option)> { + for (allowed_target_location, asset_filter) in &self.allowed_target_locations { + if destination.starts_with(&allowed_target_location.location) || + destination.eq(&allowed_target_location.location) + { + return Some((allowed_target_location, asset_filter)) + } + } + None + } +} + +/// Type represents all "stored" `BridgeConfig`s. +pub type BridgesConfig = BoundedBTreeMap>; + +/// Builder for creating `BridgesConfig`. +#[derive(Default)] +pub struct BridgesConfigBuilder { + configs: BoundedBTreeMap>, +} +impl BridgesConfigBuilder { + pub fn add_or_panic(mut self, network_id: NetworkId, bridge_config: BridgeConfig) -> Self { + self.configs.try_insert(network_id, bridge_config).expect("check bounds"); + self + } + + pub fn build(self) -> BoundedBTreeMap> { + self.configs + } +} + +/// Adapter for accessing `BridgesConfig`. +pub struct BridgesConfigAdapter(sp_std::marker::PhantomData); + +/// Adapter implementation of `ExporterFor` for `BridgesConfig` +impl> ExporterFor for BridgesConfigAdapter { + fn exporter_for( + network: &NetworkId, + _remote_location: &InteriorMultiLocation, + _message: &Xcm<()>, + ) -> Option<(MultiLocation, Option)> { + Bridges::get().get(network).map(|config| { + (config.bridge_location.location, config.bridge_location.maybe_fee.clone()) + }) + } +} + +/// Adapter implementation of `EnsureReachableDestination` for `BridgesConfig` +impl> EnsureReachableDestination for BridgesConfigAdapter { + fn ensure_destination( + remote_network: NetworkId, + remote_destination: MultiLocation, + ) -> Result { + if let Some(config) = Bridges::get().get(&remote_network) { + if let Some((allowed_target_location, _)) = + config.allowed_target_location_for(&remote_destination) + { + return Ok(ReachableDestination { + bridge: config.bridge_location.clone(), + target: allowed_target_location.clone(), + target_destination: remote_destination, + }) + } + } + Err(ReachableDestinationError::UnsupportedDestination) + } +} diff --git a/parachains/pallets/bridge-transfer/primitives/src/lib.rs b/parachains/pallets/bridge-transfer/primitives/src/lib.rs new file mode 100644 index 00000000000..9f01f25d416 --- /dev/null +++ b/parachains/pallets/bridge-transfer/primitives/src/lib.rs @@ -0,0 +1,64 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Primitives for bridge transfer pallet. + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +use xcm::prelude::*; + +mod asset_filter; +pub use asset_filter::*; + +mod config; +pub use config::*; + +/// Represents some `MultiLocation` with information if we need to pay fees or not. +#[derive(Clone, Debug, PartialEq)] +pub struct MaybePaidLocation { + pub location: MultiLocation, + pub maybe_fee: Option, +} + +/// Represents ensured/verified reachable destination. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub struct ReachableDestination { + /// Bridge location + pub bridge: MaybePaidLocation, + /// Target location (e.g. remote parachain in different consensus) + pub target: MaybePaidLocation, + /// Destination on target (e.g. account on remote parachain in different consensus) + pub target_destination: MultiLocation, +} + +/// Ensures if `remote_destination` is reachable for requested `remote_network`. +pub trait EnsureReachableDestination { + fn ensure_destination( + remote_network: NetworkId, + remote_destination: MultiLocation, + ) -> Result; +} + +/// Error type for [EnsureReachableDestination] +#[derive(Debug)] +pub enum ReachableDestinationError { + UnsupportedDestination, + UnsupportedXcmVersion, +} + +/// Reserve location as `MultiLocation` with `AssetFilter` +pub type ReserveLocation = (MultiLocation, AssetFilter); diff --git a/parachains/pallets/bridge-transfer/src/benchmarking.rs b/parachains/pallets/bridge-transfer/src/benchmarking.rs new file mode 100644 index 00000000000..10ca3d94f4e --- /dev/null +++ b/parachains/pallets/bridge-transfer/src/benchmarking.rs @@ -0,0 +1,76 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! `BridgeTransfer` pallet benchmarks. + +use crate::{BenchmarkHelper, Call, Config, Event, Pallet}; + +use frame_benchmarking::{benchmarks, BenchmarkError}; +use frame_support::{ + assert_ok, ensure, + traits::{EnsureOrigin, Get}, +}; +use sp_std::prelude::*; +use xcm::prelude::*; + +benchmarks! { + transfer_asset_via_bridge { + let _ = T::AssetTransferOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + // every asset has its own configuration and ledger, so there's a performance dependency + // (be sure to use "worst" of assets) + let max_assets_limit = T::AssetsLimit::get(); + ensure!(max_assets_limit > 0, "MaxAssetsLimit not set up correctly."); + + // get desired remote destination + let desired_bridged_location = T::BenchmarkHelper::desired_bridged_location() + .ok_or(BenchmarkError::Stop("missing `desired_bridged_location` data"))?; + + // resolve expected reserve account + let assumed_reserve_account = Pallet::::resolve_reserve_account(&desired_bridged_location.1); + + // prepare all requirements + let (origin, assets, destination) = T::BenchmarkHelper::prepare_asset_transfer_for( + desired_bridged_location.clone(), + assumed_reserve_account, + ).ok_or(BenchmarkError::Stop("missing `prepare_asset_transfer` data"))?; + + // check assets + let assets_count = match &assets { + VersionedMultiAssets::V2(assets) => assets.len(), + VersionedMultiAssets::V3(assets) => assets.len(), + }; + ensure!(assets_count == max_assets_limit as usize, "`assets` not set up correctly for worst case."); + + // check destination if configuration is ok + assert_ok!(::ensure_destination( + desired_bridged_location.0, + destination.clone().try_into().map_err(|_|BenchmarkError::Stop("invalid configuration or data"))?)); + + }: _(origin, Box::new(assets), Box::new(destination)) + verify { + // we don't care about message hash or sender cost here, just check that the transfer has been initiated + let actual_event = frame_system::Pallet::::events().pop().map(|r| r.event); + let expected_event: ::RuntimeEvent = Event::TransferInitiated { + message_id: Default::default(), + forwarded_message_id: Default::default(), + sender_cost: Default::default(), + }.into(); + assert!(matches!(actual_event, Some(expected_event))); + } + + impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::TestRuntime); +} diff --git a/parachains/pallets/bridge-transfer/src/features.rs b/parachains/pallets/bridge-transfer/src/features.rs new file mode 100644 index 00000000000..14e52950bf3 --- /dev/null +++ b/parachains/pallets/bridge-transfer/src/features.rs @@ -0,0 +1,185 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + types::{AssetTransferKind, ResolveAssetTransferKind}, + LOG_TARGET, +}; +use frame_support::traits::{ContainsPair, Get}; +use pallet_bridge_transfer_primitives::{BridgesConfig, MatchAssetLocation, ReserveLocation}; +use xcm::prelude::*; +use xcm_builder::ensure_is_remote; + +/// Adapter verifies if it is allowed to receive `MultiAsset` from `MultiLocation`. +/// +/// Note: `MultiLocation` has to be from different global consensus. +pub struct IsTrustedBridgedReserveLocationForConcreteAsset( + sp_std::marker::PhantomData<(UniversalLocation, Reserves)>, +); +impl< + UniversalLocation: Get, + Reserves: Get>, + > ContainsPair + for IsTrustedBridgedReserveLocationForConcreteAsset +{ + fn contains(asset: &MultiAsset, origin: &MultiLocation) -> bool { + let universal_source = UniversalLocation::get(); + log::trace!( + target: "xcm::contains", + "IsTrustedBridgedReserveLocationForConcreteAsset asset: {:?}, origin: {:?}, universal_source: {:?}", + asset, origin, universal_source + ); + + // check remote origin + let _ = match ensure_is_remote(universal_source, *origin) { + Ok(devolved) => devolved, + Err(_) => { + log::trace!( + target: "xcm::contains", + "IsTrustedBridgedReserveLocationForConcreteAsset origin: {:?} is not remote to the universal_source: {:?}", + origin, universal_source + ); + return false + }, + }; + + // check asset location + let asset_location = match &asset.id { + Concrete(location) => location, + _ => return false, + }; + + // check asset according to the configured reserve locations + for (reserve_location, asset_filter) in Reserves::get() { + if origin.eq(&reserve_location) { + if asset_filter.matches(asset_location) { + return true + } + } + } + + false + } +} + +/// Adapter verifies if it is allowed to transfer out `MultiAsset` to the `MultiLocation`. +/// +/// Note: `MultiLocation` has to be from different global consensus. +pub struct IsAllowedReserveBasedTransferForConcreteAssetToBridgedLocation< + UniversalLocation, + Bridges, +>(sp_std::marker::PhantomData<(UniversalLocation, Bridges)>); +impl, Bridges: Get> + ContainsPair + for IsAllowedReserveBasedTransferForConcreteAssetToBridgedLocation +{ + fn contains(asset: &MultiAsset, to_location: &MultiLocation) -> bool { + let universal_source = UniversalLocation::get(); + log::trace!( + target: "xcm::contains", + "IsAllowedReserveBasedTransferForConcreteAssetToBridgedLocation asset: {:?}, to_location: {:?}, universal_source: {:?}", + asset, to_location, universal_source + ); + + // check remote origin + let (remote_network, _) = match ensure_is_remote(universal_source, *to_location) { + Ok(devolved) => devolved, + Err(_) => { + log::trace!( + target: "xcm::contains", + "IsAllowedReserveBasedTransferForConcreteAssetToBridgedLocation to_location: {:?} is not remote to the universal_source: {:?}", + to_location, universal_source + ); + return false + }, + }; + + // check asset location + let asset_location = match &asset.id { + Concrete(location) => location, + _ => return false, + }; + + // check asset according to the config + if let Some(config) = Bridges::get().get(&remote_network) { + if let Some((_, Some(asset_filter))) = config.allowed_target_location_for(&to_location) + { + return asset_filter.matches(asset_location) + } + } + + false + } +} + +/// Implementation of `ResolveTransferKind` which tries to resolve all kinds of transfer. +pub struct ConcreteAssetTransferKindResolver< + IsReserveLocationForAsset, + IsAllowedReserveBasedTransferForAsset, +>(sp_std::marker::PhantomData<(IsReserveLocationForAsset, IsAllowedReserveBasedTransferForAsset)>); + +impl< + IsReserveLocationForAsset: ContainsPair, + IsAllowedReserveBasedTransferForAsset: ContainsPair, + > ResolveAssetTransferKind + for ConcreteAssetTransferKindResolver< + IsReserveLocationForAsset, + IsAllowedReserveBasedTransferForAsset, + > +{ + fn resolve(asset: &MultiAsset, target_location: &MultiLocation) -> AssetTransferKind { + log::trace!( + target: LOG_TARGET, + "ConcreteAssetTransferKindResolver resolve asset: {:?}, target_location: {:?}", + asset, target_location + ); + + // accepts only Concrete + match &asset.id { + Concrete(_) => (), + _ => return AssetTransferKind::Unsupported, + }; + + // check if target_location is allowed for requested asset to be transferred there + let is_reserve_based_candidate = + IsAllowedReserveBasedTransferForAsset::contains(asset, target_location); + + // check if we are trying to transfer back reserve-deposited assets + // other words: if target_location is a known reserve location for asset + let is_withdraw_reserve_candidate = + IsReserveLocationForAsset::contains(asset, target_location); + + match (is_reserve_based_candidate, is_withdraw_reserve_candidate) { + (true, false) => AssetTransferKind::ReserveBased, + (false, true) => AssetTransferKind::WithdrawReserve, + (true, true) => { + log::warn!( + target: LOG_TARGET, + "ConcreteAssetTransferKindResolver invalid configuration of transfer kind resolve for asset: {:?} and target_location: {:?} - is_reserve_based_candidate/is_withdraw_reserve_candidate: {:?}/{:?}", + asset, target_location, is_reserve_based_candidate, is_withdraw_reserve_candidate + ); + AssetTransferKind::Unsupported + }, + (false, false) => { + log::trace!( + target: LOG_TARGET, + "ConcreteAssetTransferKindResolver unsupported transfer kind for asset: {:?} and target_location: {:?} - is_reserve_based_candidate/is_withdraw_reserve_candidate: {:?}/{:?}", + asset, target_location, is_reserve_based_candidate, is_withdraw_reserve_candidate + ); + AssetTransferKind::Unsupported + }, + } + } +} diff --git a/parachains/pallets/bridge-transfer/src/impls.rs b/parachains/pallets/bridge-transfer/src/impls.rs new file mode 100644 index 00000000000..9eb0e6cfcac --- /dev/null +++ b/parachains/pallets/bridge-transfer/src/impls.rs @@ -0,0 +1,264 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + types::{AssetTransferKind, ResolveAssetTransferKind}, + Config, Error, Event, Pallet, LOG_TARGET, +}; +use frame_support::{pallet_prelude::Get, transactional}; +use frame_system::unique; +use pallet_bridge_transfer_primitives::{ + EnsureReachableDestination, ReachableDestination, ReachableDestinationError, +}; +use sp_runtime::DispatchError; +use xcm::prelude::*; +use xcm_builder::ensure_is_remote; +use xcm_executor::traits::TransactAsset; + +impl From for Error { + fn from(value: ReachableDestinationError) -> Self { + match value { + ReachableDestinationError::UnsupportedDestination => Error::::UnsupportedDestination, + ReachableDestinationError::UnsupportedXcmVersion => Error::::UnsupportedXcmVersion, + } + } +} + +impl Pallet { + /// Validates destination and check if we support bridging to this remote global consensus + /// + /// Returns: correct remote location, where we should be able to bridge + pub(crate) fn ensure_reachable_remote_destination( + remote_destination: MultiLocation, + ) -> Result> { + let devolved = ensure_is_remote(T::UniversalLocation::get(), remote_destination) + .map_err(|_| Error::::UnsupportedDestination)?; + let (remote_network, _) = devolved; + + T::BridgedDestinationValidator::ensure_destination(remote_network, remote_destination) + .map_err(Into::into) + } + + /// Tries to initiate transfer assets over bridge. + #[transactional] + pub(crate) fn initiate_transfer_asset_via_bridge_in_transaction( + origin_location: MultiLocation, + destination: ReachableDestination, + assets: MultiAssets, + ) -> Result<(), DispatchError> { + // Resolve reserve account + let reserve_account = Self::resolve_reserve_account(&destination); + + // Target destination + let target_location = destination.target.location; + + // UniversalLocation as sovereign account location on target_location (as target_location sees UniversalLocation) + let universal_location_as_sovereign_account_on_target_location = + T::UniversalLocation::get() + .invert_target(&target_location) + .map_err(|_| Error::::InvalidTargetLocation)?; + + // Prepare some XcmContext + let xcm_context = XcmContext::with_message_id(unique(reserve_account)); + + // Resolve/iterate all assets and how to transfer them + let mut reserve_assets_deposited = xcm_executor::Assets::new(); + let mut reserved_assets_for_withdrawal = xcm_executor::Assets::new(); + for asset in assets.into_inner() { + // first we need to know what kind of transfer it is + match T::AssetTransferKindResolver::resolve(&asset, &target_location) { + AssetTransferKind::ReserveBased => + // Move asset to reserve account + T::AssetTransactor::transfer_asset( + &asset, + &origin_location, + &reserve_account, + &xcm_context, + ) + .and_then(|reserved_asset| { + Self::deposit_event(Event::ReserveAssetsDeposited { + from: origin_location, + to: reserve_account, + assets: reserved_asset.clone().into(), + }); + reserve_assets_deposited.subsume_assets(reserved_asset); + Ok(()) + }) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "AssetTransactor failed to reserve assets from origin_location: {:?} to reserve_account: {:?} for assets: {:?}, error: {:?}", + origin_location, + reserve_account, + asset, + e + ); + Error::::FailedToReserve + })?, + AssetTransferKind::WithdrawReserve => { + // Just withdraw/burn asset here + T::AssetTransactor::withdraw_asset( + &asset, + &origin_location, + Some(&xcm_context), + ) + .and_then(|withdrawn_asset| { + Self::deposit_event(Event::AssetsWithdrawn { + from: origin_location, + assets: withdrawn_asset.clone().into(), + }); + reserved_assets_for_withdrawal.subsume_assets(withdrawn_asset); + Ok(()) + }) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "AssetTransactor failed to withdraw assets from origin_location: {:?} for assets: {:?}, error: {:?}", + origin_location, + asset, + e + ); + Error::::FailedToWithdraw + })? + } + AssetTransferKind::Unsupported => return Err(Error::::UnsupportedAssetTransferKind.into()), + } + } + + // Prepare xcm msg for the other side. + + // Reanchor stuff - we need to convert local asset id/MultiLocation to format that could be understood by different consensus and from their point-of-view + reserve_assets_deposited.reanchor(&target_location, T::UniversalLocation::get(), None); + reserved_assets_for_withdrawal.reanchor( + &target_location, + T::UniversalLocation::get(), + None, + ); + let remote_destination_reanchored = destination.target_destination + .reanchored(&target_location, T::UniversalLocation::get()) + .map_err(|errored_dest| { + log::error!( + target: LOG_TARGET, + "Failed to reanchor remote_destination: {:?} for target_destination: {:?} and universal_location: {:?}", + errored_dest, + target_location, + T::UniversalLocation::get() + ); + Error::::InvalidRemoteDestination + })?; + + // prepare xcm message + // 1. buy execution (if needed) -> (we expect UniversalLocation's sovereign account should pay) + let (mut xcm_instructions, maybe_buy_execution) = match destination.target.maybe_fee { + Some(target_location_fee) => ( + sp_std::vec![ + WithdrawAsset(target_location_fee.clone().into()), + BuyExecution { fees: target_location_fee.clone(), weight_limit: Unlimited }, + ], + Some(target_location_fee), + ), + None => ( + sp_std::vec![UnpaidExecution { check_origin: None, weight_limit: Unlimited }], + None, + ), + }; + + // 2. add deposit reserved asset to destination account (if any) + if !reserve_assets_deposited.is_empty() { + xcm_instructions.extend(sp_std::vec![ + ReserveAssetDeposited(reserve_assets_deposited.clone().into()), + DepositAsset { + assets: MultiAssetFilter::from(MultiAssets::from(reserve_assets_deposited)), + beneficiary: remote_destination_reanchored + }, + ]); + } + + // 3. add withdraw/move reserve from sovereign account to destination (if any) + if !reserved_assets_for_withdrawal.is_empty() { + xcm_instructions.extend(sp_std::vec![ + // we expect here, that origin is a sovereign account which was used as **reserve account** + WithdrawAsset(reserved_assets_for_withdrawal.clone().into()), + DepositAsset { + assets: MultiAssetFilter::from(MultiAssets::from( + reserved_assets_for_withdrawal + )), + beneficiary: remote_destination_reanchored + }, + ]); + } + + // 4. add return unspent weight/asset back to the UniversalLocation's sovereign account on target + if let Some(target_location_fee) = maybe_buy_execution { + xcm_instructions.extend(sp_std::vec![ + RefundSurplus, + DepositAsset { + assets: MultiAssetFilter::from(MultiAssets::from(target_location_fee)), + beneficiary: universal_location_as_sovereign_account_on_target_location + }, + ]); + } + + Self::initiate_bridge_transfer( + target_location, + xcm_context.message_id, + xcm_instructions.into(), + ) + .map_err(Into::into) + } + + /// Tries to send xcm message over bridge + fn initiate_bridge_transfer( + dest: MultiLocation, + message_id: XcmHash, + mut xcm: Xcm<()>, + ) -> Result<(), Error> { + // append message_id + xcm.0.extend(sp_std::vec![SetTopic(message_id)]); + + log::info!( + target: LOG_TARGET, + "[T::BridgeXcmSender] send to bridge, dest: {:?}, xcm: {:?}, message_id: {:?}", + dest, + xcm, + message_id, + ); + + // call bridge + // TODO: check-parameter - should we handle `sender_cost` somehow ? + let (forwarded_message_id, sender_cost) = send_xcm::(dest, xcm) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "[T::BridgeXcmSender] SendError occurred, error: {:?}", + e + ); + Error::::BridgeCallError + })?; + + // just fire event + Self::deposit_event(Event::TransferInitiated { + message_id, + forwarded_message_id, + sender_cost, + }); + Ok(()) + } + + /// Resolve (sovereign) account which will be used as reserve account + pub(crate) fn resolve_reserve_account(destination: &ReachableDestination) -> MultiLocation { + destination.target.location + } +} diff --git a/parachains/pallets/bridge-transfer/src/lib.rs b/parachains/pallets/bridge-transfer/src/lib.rs new file mode 100644 index 00000000000..12a91faa44b --- /dev/null +++ b/parachains/pallets/bridge-transfer/src/lib.rs @@ -0,0 +1,209 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Bridge Transfer Pallet +//! +//! Module supports transfer assets over bridges between different consensus chain. +//! With fine-grained configuration you can control transferred assets (out/in) between different consensus chain. +//! "Transfer asset over bridge" recognize two kinds of transfer see `types::AssetTransferKind`. + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; +pub use pallet_bridge_transfer_primitives::MaybePaidLocation; + +pub mod features; +mod impls; +mod types; +pub mod weights; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +#[cfg(test)] +mod tests; + +/// The log target of this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-transfer"; + +#[frame_support::pallet] +pub mod pallet { + pub use crate::weights::WeightInfo; + + use crate::types::ResolveAssetTransferKind; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use pallet_bridge_transfer_primitives::EnsureReachableDestination; + use sp_std::boxed::Box; + use xcm::prelude::*; + use xcm_executor::traits::TransactAsset; + + #[pallet::pallet] + pub struct Pallet(_); + + /// Everything we need to run benchmarks. + #[cfg(feature = "runtime-benchmarks")] + pub trait BenchmarkHelper { + /// Returns proper destination for NetworkId, supported by the runtime. + /// + /// We expect that the XCM environment (`BridgeXcmSender`) has everything enabled + /// to support transfer to this destination **after** `prepare_asset_transfer` call. + fn desired_bridged_location( + ) -> Option<(NetworkId, pallet_bridge_transfer_primitives::ReachableDestination)>; + + /// Prepare environment for assets transfer and return transfer origin and assets + /// to transfer. After this function is called, we expect `transfer_asset_via_bridge` + /// to succeed, so in proper environment, it should: + /// + /// - deposit enough funds (fee from `desired_bridged_location()` and transferred assets) to the sender account; + /// + /// - ensure that the `BridgeXcmSender` is properly configured for the transfer; + /// + /// - be close to the worst possible scenario - i.e. if some account may need to be created during + /// the assets transfer, it should be created. If there are multiple bridges, the "worst possible" + /// (in terms of performance) bridge must be selected for the transfer. + fn prepare_asset_transfer_for( + desired_bridged_location: ( + NetworkId, + pallet_bridge_transfer_primitives::ReachableDestination, + ), + assumed_reserve_account: MultiLocation, + ) -> Option<(RuntimeOrigin, VersionedMultiAssets, VersionedMultiLocation)>; + } + + #[cfg(feature = "runtime-benchmarks")] + impl BenchmarkHelper for () { + fn desired_bridged_location( + ) -> Option<(NetworkId, pallet_bridge_transfer_primitives::ReachableDestination)> { + None + } + + fn prepare_asset_transfer_for( + _desired_bridged_location: ( + NetworkId, + pallet_bridge_transfer_primitives::ReachableDestination, + ), + _assumed_reserve_account: MultiLocation, + ) -> Option<(RuntimeOrigin, VersionedMultiAssets, VersionedMultiLocation)> { + None + } + } + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// Runtime's universal location + type UniversalLocation: Get; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + + /// How to withdraw and deposit an asset for reserve. + /// (Config for transfer out) + type AssetTransactor: TransactAsset; + /// Transfer kind resolver for `asset` to `target_location`. + type AssetTransferKindResolver: ResolveAssetTransferKind; + /// Required origin for asset transfer. If successful, it resolves to `MultiLocation`. + /// (Config for transfer out) + type AssetTransferOrigin: EnsureOrigin; + /// Max count of assets in one call + /// (Config for transfer out) + type AssetsLimit: Get; + + /// Validates remote_destination if it is reachable from the point of configuration + type BridgedDestinationValidator: EnsureReachableDestination; + /// XCM sender which sends messages to the BridgeHub + /// (Config for transfer out) + type BridgeXcmSender: SendXcm; + + /// Benchmarks helper. + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; + } + + #[pallet::error] + #[cfg_attr(test, derive(PartialEq))] + pub enum Error { + InvalidAssets, + AssetsLimitReached, + UnsupportedDestination, + UnsupportedXcmVersion, + InvalidTargetLocation, + InvalidRemoteDestination, + BridgeCallError, + FailedToReserve, + FailedToWithdraw, + UnsupportedAssetTransferKind, + } + + #[pallet::event] + #[pallet::generate_deposit(pub (super) fn deposit_event)] + pub enum Event { + /// Transfer was successfully entered to the system (does not mean already delivered) + TransferInitiated { + /// `XcmHash` from `XcmContext` which is used for `AssetTransactor` processing and is related to the original message constructed here + message_id: XcmHash, + /// `XcmHash` from `SendXcm` (which is used for `ExportMessage` envelope) + forwarded_message_id: XcmHash, + /// `SendXcm` cost + sender_cost: MultiAssets, + }, + + /// Reserve assets passed + ReserveAssetsDeposited { from: MultiLocation, to: MultiLocation, assets: MultiAssets }, + /// Assets were withdrawn + AssetsWithdrawn { from: MultiLocation, assets: MultiAssets }, + } + + #[pallet::call] + impl Pallet { + /// Transfer asset via bridge to different global consensus + /// + /// Parameters: + /// + /// * `assets`: + /// * `destination`: Different consensus location, where the assets will be deposited, e.g. Polkadot's Statemint: `2, X2(GlobalConsensus(NetworkId::Polkadot), Parachain(1000))` + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::transfer_asset_via_bridge())] + pub fn transfer_asset_via_bridge( + origin: OriginFor, + assets: Box, + destination: Box, + ) -> DispatchResult { + // Check origin + let origin_location = T::AssetTransferOrigin::ensure_origin(origin)?; + + // Check if remote destination is reachable + let destination = Self::ensure_reachable_remote_destination( + (*destination).try_into().map_err(|()| Error::::InvalidRemoteDestination)?, + )?; + + // Check assets (lets leave others checks on `AssetTransactor`) + let assets: MultiAssets = + (*assets).try_into().map_err(|()| Error::::InvalidAssets)?; + ensure!(assets.len() <= T::AssetsLimit::get() as usize, Error::::AssetsLimitReached); + + // Do this in transaction (explicitly), the rollback should occur in case of any error and no assets will be trapped or lost + Self::initiate_transfer_asset_via_bridge_in_transaction( + origin_location, + destination, + assets, + ) + } + } +} diff --git a/parachains/pallets/bridge-transfer/src/tests.rs b/parachains/pallets/bridge-transfer/src/tests.rs new file mode 100644 index 00000000000..33ef185cd3b --- /dev/null +++ b/parachains/pallets/bridge-transfer/src/tests.rs @@ -0,0 +1,539 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate as bridge_transfer; + +use crate::{ + features::{ + ConcreteAssetTransferKindResolver, + IsAllowedReserveBasedTransferForConcreteAssetToBridgedLocation, + }, + Config, Error, Event, +}; +use frame_support::{ + assert_noop, assert_ok, + dispatch::DispatchError, + parameter_types, sp_io, sp_tracing, + traits::{ConstU32, Currency}, +}; +use pallet_bridge_transfer_primitives::{ + AssetFilter, BridgeConfig, BridgesConfig, BridgesConfigAdapter, BridgesConfigBuilder, + MaybePaidLocation, MultiLocationFilter, ReachableDestination, +}; +use polkadot_parachain::primitives::Sibling; +use sp_runtime::{ + testing::{Header, H256}, + traits::{BlakeTwo256, IdentityLookup}, + AccountId32, ModuleError, +}; +use sp_version::RuntimeVersion; +use xcm::prelude::*; +use xcm_builder::{ + AccountId32Aliases, CurrencyAdapter, EnsureXcmOrigin, GlobalConsensusParachainConvertsFor, + IsConcrete, SiblingParachainConvertsVia, SignedToAccountId32, UnpaidRemoteExporter, +}; +use xcm_executor::traits::ConvertLocation; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum TestRuntime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + BridgeTransfer: bridge_transfer::{Pallet, Call, Event} = 52, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub Version: RuntimeVersion = RuntimeVersion { + spec_name: sp_version::create_runtime_str!("test"), + impl_name: sp_version::create_runtime_str!("system-test"), + authoring_version: 1, + spec_version: 1, + impl_version: 1, + apis: sp_version::create_apis_vec!([]), + transaction_version: 1, + state_version: 1, + }; +} + +pub type AccountId = AccountId32; + +impl frame_system::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockLength = (); + type BlockWeights = (); + type Version = Version; + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 5; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for TestRuntime { + type Balance = u64; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = RuntimeHoldReason; + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; +} + +parameter_types! { + pub const BridgedNetwork: NetworkId = NetworkId::ByGenesis([4; 32]); + pub BridgeLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(1013))); + pub TargetLocation: MultiLocation = MultiLocation::new(2, X2(GlobalConsensus(BridgedNetwork::get()), Parachain(1000))); + // pub TargetLocationFee: Option = Some((MultiLocation::parent(), 1_000_000).into()); + pub TargetLocationFee: Option = None; + + pub const RelayNetwork: NetworkId = NetworkId::ByGenesis([9; 32]); + pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(RelayNetwork::get()), Parachain(1000)); + // Relay chain currency/balance location (e.g. KsmLocation, DotLocation, ..) + pub const RelayLocation: MultiLocation = MultiLocation::parent(); + + pub Bridges: BridgesConfig = BridgesConfigBuilder::default() + .add_or_panic( + BridgedNetwork::get(), + BridgeConfig::new( + MaybePaidLocation { + location: BridgeLocation::get(), + maybe_fee: None, + } + ).add_target_location( + MaybePaidLocation { + location: TargetLocation::get(), + maybe_fee: TargetLocationFee::get(), + }, + Some(AssetFilter::ByMultiLocation( + MultiLocationFilter::default() + // allow transfer parent/relay native token + .add_equals(MultiLocation::parent()) + )) + ) + ) + .build(); + +} + +std::thread_local! { + static ROUTED_MESSAGE: std::cell::RefCell>> = std::cell::RefCell::new(None); + static NOT_APPLICABLE_AS_SOME_OR_FAIL_ROUTER_SWITCH: std::cell::RefCell> = std::cell::RefCell::new(Some(())); +} + +pub struct ThreadLocalXcmRouter; +impl SendXcm for ThreadLocalXcmRouter { + type Ticket = Option>; + + fn validate( + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { + log::info!( + target: super::LOG_TARGET, + "[ThreadLocalXcmRouter]: destination: {:?}, message: {:?}", + destination, + message + ); + Ok((message.take(), MultiAssets::default())) + } + + fn deliver(ticket: Self::Ticket) -> Result { + match ticket { + Some(msg) => { + ROUTED_MESSAGE.with(|rm| *rm.borrow_mut() = Some(msg)); + Ok([0u8; 32]) + }, + None => Err(SendError::MissingArgument), + } + } +} + +pub struct NotApplicableOrFailRouter; +impl SendXcm for NotApplicableOrFailRouter { + type Ticket = Option>; + + fn validate( + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { + log::info!( + target: super::LOG_TARGET, + "[NotApplicableOrFailRouter]: destination: {:?}, message: {:?}", + destination, + message + ); + + let wanna_fail = + NOT_APPLICABLE_AS_SOME_OR_FAIL_ROUTER_SWITCH.with(|s| s.borrow().is_none()); + if wanna_fail { + Err(SendError::Transport("Simulate what ever error")) + } else { + Err(SendError::NotApplicable) + } + } + + fn deliver(ticket: Self::Ticket) -> Result { + unimplemented!("We should not come here, ticket: {:?}", ticket) + } +} + +pub type XcmRouter = (NotApplicableOrFailRouter, ThreadLocalXcmRouter); + +/// Bridge router, which wraps and sends xcm to BridgeHub to be delivered to the different GlobalConsensus +pub type TestBridgeXcmSender = + UnpaidRemoteExporter, XcmRouter, UniversalLocation>; + +/// No local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = SignedToAccountId32; + +pub type LocationToAccountId = ( + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, + // Different global consensus parachain sovereign account. + // (Used for over-bridge transfers and reserve processing) + GlobalConsensusParachainConvertsFor, +); + +/// Means for transacting the native currency 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, + // 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 don't track any teleports of `Balances`. + (), +>; + +parameter_types! { + // na constantu + pub const AssetsLimit: u8 = 1; +} + +impl Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; + type WeightInfo = (); + type AssetTransactor = CurrencyTransactor; + type AssetTransferKindResolver = ConcreteAssetTransferKindResolver< + (), + IsAllowedReserveBasedTransferForConcreteAssetToBridgedLocation, + >; + type AssetTransferOrigin = EnsureXcmOrigin; + type AssetsLimit = AssetsLimit; + type BridgedDestinationValidator = BridgesConfigAdapter; + type BridgeXcmSender = TestBridgeXcmSender; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + +pub(crate) fn new_test_ext() -> sp_io::TestExternalities { + sp_tracing::try_init_simple(); + let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + // with 0 block_number events dont work + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + frame_system::Pallet::::set_block_number(1u32.into()); + }); + + ext +} + +fn account(account: u8) -> AccountId32 { + AccountId32::new([account; 32]) +} + +fn consensus_account(network: NetworkId, account: u8) -> Junction { + xcm::prelude::AccountId32 { network: Some(network), id: AccountId32::new([account; 32]).into() } +} + +#[test] +fn test_ensure_reachable_remote_destination() { + new_test_ext().execute_with(|| { + let bridged_network = BridgedNetwork::get(); + + // v3 - "parent: 0" wrong + assert_eq!( + BridgeTransfer::ensure_reachable_remote_destination(MultiLocation::new( + 0, + X2(GlobalConsensus(bridged_network), Parachain(1000)) + )), + Err(Error::::UnsupportedDestination) + ); + // v3 - "parent: 1" wrong + assert_eq!( + BridgeTransfer::ensure_reachable_remote_destination(MultiLocation::new( + 1, + X2(GlobalConsensus(bridged_network), Parachain(1000)) + )), + Err(Error::::UnsupportedDestination) + ); + + // v3 - Rococo is not supported + assert_eq!( + BridgeTransfer::ensure_reachable_remote_destination(MultiLocation::new( + 2, + X2(GlobalConsensus(Rococo), Parachain(1000)) + )), + Err(Error::::UnsupportedDestination) + ); + + // v3 - remote_destination is not allowed + assert_eq!( + BridgeTransfer::ensure_reachable_remote_destination(MultiLocation::new( + 2, + X2(GlobalConsensus(bridged_network), Parachain(1234)) + )), + Err(Error::::UnsupportedDestination) + ); + + // v3 - ok (allowed) + assert_ok!( + BridgeTransfer::ensure_reachable_remote_destination(MultiLocation::new( + 2, + X3( + GlobalConsensus(bridged_network), + Parachain(1000), + consensus_account(bridged_network, 35) + ) + ),), + ReachableDestination { + bridge: MaybePaidLocation { location: BridgeLocation::get(), maybe_fee: None }, + target: MaybePaidLocation { + location: MultiLocation::new( + 2, + X2(GlobalConsensus(bridged_network), Parachain(1000)) + ), + maybe_fee: TargetLocationFee::get(), + }, + target_destination: MultiLocation::new( + 2, + X3( + GlobalConsensus(bridged_network), + Parachain(1000), + consensus_account(bridged_network, 35) + ) + ), + } + ); + }) +} + +#[test] +fn test_transfer_asset_via_bridge_for_currency_works() { + new_test_ext().execute_with(|| { + // initialize some Balances for user_account + let user_account = account(1); + let user_account_init_balance = 1000_u64; + let _ = Balances::deposit_creating(&user_account, user_account_init_balance); + let user_free_balance = Balances::free_balance(&user_account); + let balance_to_transfer = 15_u64; + assert!((user_free_balance - balance_to_transfer) >= ExistentialDeposit::get()); + // because, sovereign account needs to have ED otherwise reserve fails + assert!(balance_to_transfer >= ExistentialDeposit::get()); + + let bridged_network = BridgedNetwork::get(); + let target_location = TargetLocation::get(); + + // checks before + assert!(ROUTED_MESSAGE.with(|r| r.borrow().is_none())); + assert_eq!(Balances::free_balance(&user_account), user_account_init_balance); + let reserve_account = LocationToAccountId::convert_location(&target_location) + .expect("converted target_location as accountId"); + assert_eq!(Balances::free_balance(&reserve_account), 0); + + // trigger transfer_asset_via_bridge - should trigger new ROUTED_MESSAGE + let asset = MultiAsset { + fun: Fungible(balance_to_transfer.into()), + id: Concrete(RelayLocation::get()), + }; + let assets = Box::new(VersionedMultiAssets::from(MultiAssets::from(asset))); + + // destination is account from different consensus + let destination = Box::new(VersionedMultiLocation::from(MultiLocation::new( + 2, + X3( + GlobalConsensus(bridged_network), + Parachain(1000), + consensus_account(bridged_network, 2), + ), + ))); + + // trigger asset transfer + assert_ok!(BridgeTransfer::transfer_asset_via_bridge( + RuntimeOrigin::signed(account(1)), + assets, + destination, + )); + + // check user account decressed + assert_eq!( + Balances::free_balance(&user_account), + user_account_init_balance - balance_to_transfer + ); + // check reserve account increased + assert_eq!(Balances::free_balance(&reserve_account), 15); + + // check events + let events = System::events(); + assert!(!events.is_empty()); + + // check reserve asset deposited event + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::BridgeTransfer(Event::ReserveAssetsDeposited { .. }) + ))); + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::BridgeTransfer(Event::TransferInitiated { .. }) + ))); + + // check fired XCM ExportMessage to bridge-hub + let fired_xcm = + ROUTED_MESSAGE.with(|r| r.take().expect("xcm::ExportMessage should be here")); + + if let Some(ExportMessage { xcm, .. }) = fired_xcm.0.iter().find(|instr| { + matches!( + instr, + ExportMessage { + network, + destination: X1(Parachain(1000)), + .. + } if network == &bridged_network + ) + }) { + println!("{:?}", xcm.0); + assert!(xcm.0.iter().any(|instr| matches!(instr, UnpaidExecution { .. }))); + assert!(xcm.0.iter().any(|instr| matches!(instr, ReserveAssetDeposited(..)))); + assert!(xcm.0.iter().any(|instr| matches!(instr, DepositAsset { .. }))); + assert!(xcm.0.iter().any(|instr| matches!(instr, SetTopic { .. }))); + } else { + assert!(false, "Does not contains [`ExportMessage`], fired_xcm: {:?}", fired_xcm); + } + }); +} + +#[test] +fn test_transfer_asset_via_bridge_in_case_of_error_transactional_works() { + new_test_ext().execute_with(|| { + // initialize some Balances for user_account + let user_account = account(1); + let user_account_init_balance = 1000_u64; + let _ = Balances::deposit_creating(&user_account, user_account_init_balance); + let user_free_balance = Balances::free_balance(&user_account); + let balance_to_transfer = 15_u64; + assert!((user_free_balance - balance_to_transfer) >= ExistentialDeposit::get()); + // because, sovereign account needs to have ED otherwise reserve fails + assert!(balance_to_transfer >= ExistentialDeposit::get()); + + let bridged_network = BridgedNetwork::get(); + let target_location = TargetLocation::get(); + + // checks before + assert!(ROUTED_MESSAGE.with(|r| r.borrow().is_none())); + let user_balance_before = Balances::free_balance(&user_account); + assert_eq!(user_balance_before, user_account_init_balance); + let reserve_account = LocationToAccountId::convert_location(&target_location) + .expect("converted target_location as accountId"); + let reserve_account_before = Balances::free_balance(&reserve_account); + assert_eq!(reserve_account_before, 0); + + // trigger transfer_asset_via_bridge - should trigger new ROUTED_MESSAGE + let asset = MultiAsset { + fun: Fungible(balance_to_transfer.into()), + id: Concrete(RelayLocation::get()), + }; + let assets = Box::new(VersionedMultiAssets::from(MultiAssets::from(asset))); + + // destination is account from different consensus + let destination = Box::new(VersionedMultiLocation::from(MultiLocation::new( + 2, + X3( + GlobalConsensus(bridged_network), + Parachain(1000), + consensus_account(bridged_network, 2), + ), + ))); + + // reset events + System::reset_events(); + + // Simulate XcmRouter failure + NOT_APPLICABLE_AS_SOME_OR_FAIL_ROUTER_SWITCH.with(|s| *s.borrow_mut() = None); + + // trigger asset transfer + assert_noop!( + BridgeTransfer::transfer_asset_via_bridge( + RuntimeOrigin::signed(account(1)), + assets, + destination + ), + DispatchError::Module(ModuleError { + index: 52, + error: [6, 0, 0, 0], + message: Some("BridgeCallError") + }) + ); + + // checks after + // balances are untouched + assert_eq!(Balances::free_balance(&user_account), user_balance_before); + assert_eq!(Balances::free_balance(&reserve_account), reserve_account_before); + // no xcm messages fired + assert!(ROUTED_MESSAGE.with(|r| r.borrow().is_none())); + // check events (no events because of rollback) + assert!(System::events().is_empty()); + }); +} diff --git a/parachains/pallets/bridge-transfer/src/types.rs b/parachains/pallets/bridge-transfer/src/types.rs new file mode 100644 index 00000000000..785d3ecf714 --- /dev/null +++ b/parachains/pallets/bridge-transfer/src/types.rs @@ -0,0 +1,38 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use xcm::prelude::*; + +/// Pallet support two kinds of transfer. +#[cfg_attr(feature = "std", derive(Debug, PartialEq))] +pub enum AssetTransferKind { + /// When we need to do a **reserve** on source chain to **reserve account** and then send it as `ReserveAssetDeposited` + ReserveBased, + /// When we need to do a opposite direction, withdraw/burn asset on source chain and send it as `Withdraw/Burn` on target chain from **reserve account**. + WithdrawReserve, + /// If not supported/permitted (e.g. missing configuration of trusted reserve location, ...). + Unsupported, +} + +/// Trait for resolving a transfer type for `asset` to `target_location` +pub trait ResolveAssetTransferKind { + fn resolve(asset: &MultiAsset, target_location: &MultiLocation) -> AssetTransferKind; +} + +impl ResolveAssetTransferKind for () { + fn resolve(_asset: &MultiAsset, _target_location: &MultiLocation) -> AssetTransferKind { + AssetTransferKind::Unsupported + } +} diff --git a/parachains/pallets/bridge-transfer/src/weights.rs b/parachains/pallets/bridge-transfer/src/weights.rs new file mode 100644 index 00000000000..f3667a5daa3 --- /dev/null +++ b/parachains/pallets/bridge-transfer/src/weights.rs @@ -0,0 +1,36 @@ +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Weights trait for the `pallet_bridge_assets_transfer` pallet. + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_bridge_assets_transfer. +pub trait WeightInfo { + /// Weight of the `transfer_asset_via_bridge` call. + fn transfer_asset_via_bridge() -> Weight; +} + +// Zero weights to use in tests +impl WeightInfo for () { + fn transfer_asset_via_bridge() -> Weight { + Weight::zero() + } +} diff --git a/parachains/runtimes/assets/asset-hub-kusama/Cargo.toml b/parachains/runtimes/assets/asset-hub-kusama/Cargo.toml index 0433e814a74..b7f63cfc8d7 100644 --- a/parachains/runtimes/assets/asset-hub-kusama/Cargo.toml +++ b/parachains/runtimes/assets/asset-hub-kusama/Cargo.toml @@ -74,9 +74,12 @@ pallet-collator-selection = { path = "../../../../pallets/collator-selection", d parachain-info = { path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } assets-common = { path = "../common", default-features = false } +pallet-bridge-transfer = { path = "../../../pallets/bridge-transfer", default-features = false } +pallet-bridge-transfer-primitives = { path = "../../../pallets/bridge-transfer/primitives", default-features = false } [dev-dependencies] asset-test-utils = { path = "../test-utils"} +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } [build-dependencies] substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master", optional = true } @@ -107,12 +110,15 @@ runtime-benchmarks = [ "pallet-xcm/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", + "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-session-benchmarking/runtime-benchmarks", + "cumulus-pallet-parachain-system/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "pallet-state-trie-migration/runtime-benchmarks", "assets-common/runtime-benchmarks", + "pallet-bridge-transfer/runtime-benchmarks", ] try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", @@ -140,6 +146,7 @@ try-runtime = [ "pallet-xcm/try-runtime", "parachain-info/try-runtime", "pallet-state-trie-migration/try-runtime", + "pallet-bridge-transfer/try-runtime", ] std = [ "codec/std", @@ -196,5 +203,7 @@ std = [ "parachain-info/std", "parachains-common/std", "assets-common/std", + "pallet-bridge-transfer/std", + "pallet-bridge-transfer-primitives/std", "substrate-wasm-builder", ] diff --git a/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs b/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs index 5490a1b63ea..b97def8146a 100644 --- a/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs +++ b/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs @@ -72,8 +72,9 @@ use parachains_common::{ NORMAL_DISPATCH_RATIO, SLOT_DURATION, }; use xcm_config::{ - FellowshipLocation, ForeignAssetsConvertedConcreteId, GovernanceLocation, KsmLocation, - TrustBackedAssetsConvertedConcreteId, XcmConfig, + bridging, AssetTransactors, FellowshipLocation, ForeignAssetsConvertedConcreteId, + GovernanceLocation, KsmLocation, LocalOriginToLocation, TrustBackedAssetsConvertedConcreteId, + UniversalLocation, XcmConfig, }; #[cfg(any(feature = "std", test))] @@ -83,6 +84,7 @@ pub use sp_runtime::BuildStorage; use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use xcm::latest::BodyId; +use xcm_builder::EnsureXcmOrigin; use xcm_executor::XcmExecutor; use crate::xcm_config::ForeignCreatorsSovereignAccountOf; @@ -703,6 +705,25 @@ impl pallet_nfts::Config for Runtime { type Helper = (); } +impl pallet_bridge_transfer::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; + type WeightInfo = weights::pallet_bridge_transfer::WeightInfo; + type AssetTransactor = AssetTransactors; + type AssetTransferKindResolver = + pallet_bridge_transfer::features::ConcreteAssetTransferKindResolver< + bridging::IsTrustedBridgedReserveLocationForConcreteAsset, + pallet_bridge_transfer::features::IsAllowedReserveBasedTransferForConcreteAssetToBridgedLocation, + >; + type AssetTransferOrigin = EnsureXcmOrigin; + type AssetsLimit = ConstU8<1>; + type BridgedDestinationValidator = + pallet_bridge_transfer_primitives::BridgesConfigAdapter; + type BridgeXcmSender = bridging::BridgeXcmSender; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = xcm_config::BridgeTransferBenchmarksHelper; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime where @@ -747,6 +768,7 @@ construct_runtime!( Uniques: pallet_uniques::{Pallet, Call, Storage, Event} = 51, Nfts: pallet_nfts::{Pallet, Call, Storage, Event} = 52, ForeignAssets: pallet_assets::::{Pallet, Call, Storage, Event} = 53, + BridgeTransfer: pallet_bridge_transfer::{Pallet, Call, Storage, Event} = 54, #[cfg(feature = "state-trie-version-1")] StateTrieMigration: pallet_state_trie_migration = 70, @@ -810,6 +832,7 @@ mod benches { [pallet_timestamp, Timestamp] [pallet_collator_selection, CollatorSelection] [cumulus_pallet_xcmp_queue, XcmpQueue] + [pallet_bridge_transfer, BridgeTransfer] // XCM [pallet_xcm, PolkadotXcm] // NOTE: Make sure you point to the individual modules below. @@ -1123,7 +1146,10 @@ impl_runtime_apis! { } fn universal_alias() -> Result<(MultiLocation, Junction), BenchmarkError> { - Err(BenchmarkError::Skip) + match xcm_config::BridgeTransferBenchmarksHelper::prepare_universal_alias() { + Some(alias) => Ok(alias), + None => Err(BenchmarkError::Skip) + } } fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> { diff --git a/parachains/runtimes/assets/asset-hub-kusama/src/weights/mod.rs b/parachains/runtimes/assets/asset-hub-kusama/src/weights/mod.rs index 92af360ced1..518bd88bc2e 100644 --- a/parachains/runtimes/assets/asset-hub-kusama/src/weights/mod.rs +++ b/parachains/runtimes/assets/asset-hub-kusama/src/weights/mod.rs @@ -4,6 +4,7 @@ pub mod extrinsic_weights; pub mod frame_system; pub mod pallet_assets; pub mod pallet_balances; +pub mod pallet_bridge_transfer; pub mod pallet_collator_selection; pub mod pallet_multisig; pub mod pallet_nfts; diff --git a/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_bridge_transfer.rs b/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_bridge_transfer.rs new file mode 100644 index 00000000000..47938fd1066 --- /dev/null +++ b/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_bridge_transfer.rs @@ -0,0 +1,82 @@ +// Copyright 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 . + +//! Autogenerated weights for `pallet_bridge_transfer` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-06-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-kusama-dev"), DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_bridge_transfer +// --chain=asset-hub-kusama-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/assets/asset-hub-kusama/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_bridge_transfer`. +pub struct WeightInfo(PhantomData); +impl pallet_bridge_transfer::WeightInfo for WeightInfo { + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: unknown `0x1fd0e9ea288ed87ef4c94f1a9a004f86` (r:1 w:0) + /// Proof Skipped: unknown `0x1fd0e9ea288ed87ef4c94f1a9a004f86` (r:1 w:0) + /// Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + /// Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: PolkadotXcm SupportedVersion (r:1 w:0) + /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + /// Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + /// Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainSystem RelevantMessagingState (r:1 w:0) + /// Proof Skipped: ParachainSystem RelevantMessagingState (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: XcmpQueue OutboundXcmpStatus (r:1 w:1) + /// Proof Skipped: XcmpQueue OutboundXcmpStatus (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: XcmpQueue OutboundXcmpMessages (r:0 w:1) + /// Proof Skipped: XcmpQueue OutboundXcmpMessages (max_values: None, max_size: None, mode: Measured) + fn transfer_asset_via_bridge() -> Weight { + // Proof Size summary in bytes: + // Measured: `595` + // Estimated: `6196` + // Minimum execution time: 155_490_000 picoseconds. + Weight::from_parts(158_516_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(10)) + .saturating_add(T::DbWeight::get().writes(6)) + } +} diff --git a/parachains/runtimes/assets/asset-hub-kusama/src/weights/xcm/mod.rs b/parachains/runtimes/assets/asset-hub-kusama/src/weights/xcm/mod.rs index 4bdda361a2b..94ed4aa5878 100644 --- a/parachains/runtimes/assets/asset-hub-kusama/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/asset-hub-kusama/src/weights/xcm/mod.rs @@ -214,7 +214,7 @@ impl XcmWeightInfo for AssetHubKusamaXcmWeight { XcmGeneric::::clear_transact_status() } fn universal_origin(_: &Junction) -> Weight { - Weight::MAX + XcmGeneric::::universal_origin() } fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight { Weight::MAX diff --git a/parachains/runtimes/assets/asset-hub-kusama/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/asset-hub-kusama/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 3bc1a9768fd..329e3da1679 100644 --- a/parachains/runtimes/assets/asset-hub-kusama/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/asset-hub-kusama/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,26 +17,27 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-06-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm6`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-kusama-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=asset-hub-kusama-dev +// --steps=50 +// --repeat=20 +// --extrinsic=* // --execution=wasm // --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::generic -// --extrinsic=* -// --steps=50 -// --repeat=20 -// --json +// --chain=asset-hub-kusama-dev // --header=./file_header.txt -// --output=./parachains/runtimes/assets/asset-hub-kusama/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +// --template=./templates/xcm-bench-template.hbs +// --output=./parachains/runtimes/assets/asset-hub-kusama/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,6 +51,8 @@ pub struct WeightInfo(PhantomData); impl WeightInfo { // Storage: ParachainInfo ParachainId (r:1 w:0) // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -64,17 +67,17 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `75` // Estimated: `3540` - // Minimum execution time: 340_875_000 picoseconds. - Weight::from_parts(349_790_000, 3540) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) + // Minimum execution time: 347_943_000 picoseconds. + Weight::from_parts(348_788_000, 3540) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) } pub fn buy_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_898_000 picoseconds. - Weight::from_parts(3_994_000, 0) + // Minimum execution time: 3_881_000 picoseconds. + Weight::from_parts(3_982_000, 0) } // Storage: PolkadotXcm Queries (r:1 w:0) // Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) @@ -82,61 +85,63 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `69` // Estimated: `3534` - // Minimum execution time: 10_760_000 picoseconds. - Weight::from_parts(11_070_000, 3534) + // Minimum execution time: 11_038_000 picoseconds. + Weight::from_parts(11_244_000, 3534) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 13_340_000 picoseconds. - Weight::from_parts(13_787_000, 0) + // Minimum execution time: 13_223_000 picoseconds. + Weight::from_parts(13_610_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_205_000 picoseconds. - Weight::from_parts(4_328_000, 0) + // Minimum execution time: 4_202_000 picoseconds. + Weight::from_parts(4_333_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_675_000 picoseconds. - Weight::from_parts(2_761_000, 0) + // Minimum execution time: 2_794_000 picoseconds. + Weight::from_parts(2_860_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_648_000 picoseconds. - Weight::from_parts(2_723_000, 0) + // Minimum execution time: 2_717_000 picoseconds. + Weight::from_parts(2_787_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_675_000 picoseconds. - Weight::from_parts(2_866_000, 0) + // Minimum execution time: 2_722_000 picoseconds. + Weight::from_parts(2_818_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_584_000 picoseconds. - Weight::from_parts(3_679_000, 0) + // Minimum execution time: 3_664_000 picoseconds. + Weight::from_parts(3_775_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_686_000 picoseconds. - Weight::from_parts(2_750_000, 0) + // Minimum execution time: 2_703_000 picoseconds. + Weight::from_parts(2_792_000, 0) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -151,10 +156,10 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `75` // Estimated: `3540` - // Minimum execution time: 24_994_000 picoseconds. - Weight::from_parts(25_553_000, 3540) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) + // Minimum execution time: 27_482_000 picoseconds. + Weight::from_parts(28_077_000, 3540) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) } // Storage: PolkadotXcm AssetTraps (r:1 w:1) // Proof Skipped: PolkadotXcm AssetTraps (max_values: None, max_size: None, mode: Measured) @@ -162,8 +167,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `126` // Estimated: `3591` - // Minimum execution time: 15_960_000 picoseconds. - Weight::from_parts(16_240_000, 3591) + // Minimum execution time: 15_603_000 picoseconds. + Weight::from_parts(15_899_000, 3591) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -171,11 +176,13 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_659_000 picoseconds. - Weight::from_parts(2_732_000, 0) + // Minimum execution time: 2_679_000 picoseconds. + Weight::from_parts(2_755_000, 0) } // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -190,10 +197,10 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `75` // Estimated: `3540` - // Minimum execution time: 28_083_000 picoseconds. - Weight::from_parts(28_531_000, 3540) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) + // Minimum execution time: 30_136_000 picoseconds. + Weight::from_parts(30_688_000, 3540) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(4)) } // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) @@ -201,12 +208,14 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_884_000 picoseconds. - Weight::from_parts(4_996_000, 0) + // Minimum execution time: 4_817_000 picoseconds. + Weight::from_parts(4_978_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -221,48 +230,50 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `75` // Estimated: `3540` - // Minimum execution time: 383_046_000 picoseconds. - Weight::from_parts(397_796_000, 3540) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) + // Minimum execution time: 387_992_000 picoseconds. + Weight::from_parts(392_283_000, 3540) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 118_811_000 picoseconds. - Weight::from_parts(121_240_000, 0) + // Minimum execution time: 118_538_000 picoseconds. + Weight::from_parts(121_013_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_406_000 picoseconds. - Weight::from_parts(12_637_000, 0) + // Minimum execution time: 12_087_000 picoseconds. + Weight::from_parts(12_313_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_758_000 picoseconds. - Weight::from_parts(2_831_000, 0) + // Minimum execution time: 2_784_000 picoseconds. + Weight::from_parts(2_840_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_703_000 picoseconds. - Weight::from_parts(2_798_000, 0) + // Minimum execution time: 2_693_000 picoseconds. + Weight::from_parts(2_781_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_892_000 picoseconds. - Weight::from_parts(2_930_000, 0) + // Minimum execution time: 2_913_000 picoseconds. + Weight::from_parts(2_979_000, 0) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -277,20 +288,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `75` // Estimated: `3540` - // Minimum execution time: 28_960_000 picoseconds. - Weight::from_parts(29_596_000, 3540) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) + // Minimum execution time: 31_021_000 picoseconds. + Weight::from_parts(31_786_000, 3540) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) } pub fn expect_pallet() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_303_000 picoseconds. - Weight::from_parts(5_444_000, 0) + // Minimum execution time: 5_201_000 picoseconds. + Weight::from_parts(5_335_000, 0) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -305,44 +318,54 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `75` // Estimated: `3540` - // Minimum execution time: 25_221_000 picoseconds. - Weight::from_parts(25_842_000, 3540) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) + // Minimum execution time: 27_837_000 picoseconds. + Weight::from_parts(28_360_000, 3540) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) } pub fn clear_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_734_000 picoseconds. - Weight::from_parts(2_787_000, 0) + // Minimum execution time: 2_727_000 picoseconds. + Weight::from_parts(2_817_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_625_000 picoseconds. - Weight::from_parts(2_692_000, 0) + // Minimum execution time: 2_659_000 picoseconds. + Weight::from_parts(2_701_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_638_000 picoseconds. - Weight::from_parts(2_705_000, 0) + // Minimum execution time: 2_714_000 picoseconds. + Weight::from_parts(2_806_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_104_000 picoseconds. + Weight::from_parts(5_236_000, 1489) + .saturating_add(T::DbWeight::get().reads(1)) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_666_000 picoseconds. - Weight::from_parts(2_738_000, 0) + // Minimum execution time: 2_694_000 picoseconds. + Weight::from_parts(2_747_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_857_000 picoseconds. - Weight::from_parts(2_966_000, 0) + // Minimum execution time: 2_937_000 picoseconds. + Weight::from_parts(2_992_000, 0) } } diff --git a/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs b/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs index 49fd807b6f4..016e08d9deb 100644 --- a/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs +++ b/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs @@ -35,11 +35,11 @@ use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, - EnsureXcmOrigin, FungiblesAdapter, HashedDescription, IsConcrete, LocalMint, NativeAsset, - NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + EnsureXcmOrigin, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, + IsConcrete, LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -69,6 +69,9 @@ pub type LocationToAccountId = ( AccountId32Aliases, // Foreign locations alias into accounts according to a hash of their standard description. HashedDescription>, + // Different global consensus parachain sovereign account. + // (Used for over-bridge transfers and reserve processing) + GlobalConsensusParachainConvertsFor, ); /// Means for transacting the native currency on this chain. @@ -196,6 +199,14 @@ impl Contains for SafeCallFilter { } } + // Allow to change dedicated storage items (called by governance-like) + match call { + RuntimeCall::System(frame_system::Call::set_storage { items }) + if items.iter().any(|(k, _)| k.eq(&bridging::AssetHubPolkadotMaxFee::key())) => + return true, + _ => (), + }; + matches!( call, RuntimeCall::PolkadotXcm(pallet_xcm::Call::force_xcm_version { .. }) | @@ -379,10 +390,9 @@ impl xcm_executor::Config for XcmConfig { type XcmSender = XcmRouter; type AssetTransactor = AssetTransactors; type OriginConverter = XcmOriginToTransactDispatchOrigin; - // Asset Hub Kusama does not recognize a reserve location for any asset. This does not prevent // Asset Hub acting _as_ a reserve location for KSM and assets created under `pallet-assets`. // For KSM, users must use teleport where allowed (e.g. with the Relay Chain). - type IsReserve = (); + type IsReserve = bridging::IsTrustedBridgedReserveLocationForConcreteAsset; // We allow: // - teleportation of KSM // - teleportation of sibling parachain's assets (as ForeignCreators) @@ -421,7 +431,7 @@ impl xcm_executor::Config for XcmConfig { type AssetExchanger = (); type FeeManager = (); type MessageExporter = (); - type UniversalAliases = Nothing; + type UniversalAliases = bridging::BridgedUniversalAliases; type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; @@ -500,3 +510,185 @@ impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { MultiLocation { parents: 1, interior: X1(Parachain(id)) } } } + +/// All configuration related to bridging +pub mod bridging { + use super::*; + use pallet_bridge_transfer_primitives::{ + AssetFilter, BridgeConfig, BridgesConfig, BridgesConfigAdapter, BridgesConfigBuilder, + MaybePaidLocation, MultiLocationFilter, ReserveLocation, + }; + use sp_std::collections::btree_set::BTreeSet; + use xcm_builder::UnpaidRemoteExporter; + + parameter_types! { + pub BridgeHubKusamaParaId: u32 = 1002; + pub BridgeHubKusama: MultiLocation = MultiLocation::new(1, X1(Parachain(BridgeHubKusamaParaId::get()))); + pub const PolkadotNetwork: NetworkId = NetworkId::Polkadot; + pub AssetHubPolkadot: MultiLocation = MultiLocation::new(2, X2(GlobalConsensus(PolkadotNetwork::get()), Parachain(1000))); + // Initial value, this will be adjusted by governance motion on deployment with some more accurate value + pub storage AssetHubPolkadotMaxFee: Option = Some((MultiLocation::parent(), 1_000_000).into()); + pub DotLocation: MultiLocation = MultiLocation::new(2, X1(GlobalConsensus(PolkadotNetwork::get()))); + + // Setup bridges configuration + // (hard-coded version - on-chain configuration will come later as separate feature) + pub Bridges: BridgesConfig = BridgesConfigBuilder::default() + // add exporter for Polkadot + .add_or_panic( + PolkadotNetwork::get(), + BridgeConfig::new( + MaybePaidLocation { + location: BridgeHubKusama::get(), + // Noe fees needed because we use `UnpaidRemoteExporter` and BridgeHubKusama allows unpaid execution for local system parachains + maybe_fee: None, + } + ).add_target_location( + // add target location as AssetHubPolkadot + MaybePaidLocation { + location: AssetHubPolkadot::get(), + maybe_fee: AssetHubPolkadotMaxFee::get(), + }, + Some(AssetFilter::ByMultiLocation( + MultiLocationFilter::default() + // allow transfer KSM + .add_equals(KsmLocation::get()) + )) + ) + ) + .build(); + + // Setup trusted bridged reserve locations + pub BridgedReserves: sp_std::vec::Vec = sp_std::vec![ + // trust assets from AssetHubPolkadot + ( + AssetHubPolkadot::get(), + AssetFilter::ByMultiLocation( + MultiLocationFilter::default() + // allow receive DOT + .add_equals(DotLocation::get()) + ) + ) + ]; + + /// Universal aliases + pub BridgedUniversalAliases: BTreeSet<(MultiLocation, Junction)> = BTreeSet::from_iter( + sp_std::vec![ + (BridgeHubKusama::get(), GlobalConsensus(PolkadotNetwork::get())) + ] + ); + } + + impl Contains<(MultiLocation, Junction)> for BridgedUniversalAliases { + fn contains(alias: &(MultiLocation, Junction)) -> bool { + BridgedUniversalAliases::get().contains(alias) + } + } + + /// Bridge router, which wraps and sends xcm to BridgeHub to be delivered to the different GlobalConsensus + pub type BridgeXcmSender = + UnpaidRemoteExporter, XcmRouter, UniversalLocation>; + + /// Reserve locations filter for `xcm_executor::Config::IsReserve`. + pub type IsTrustedBridgedReserveLocationForConcreteAsset = + pallet_bridge_transfer::features::IsTrustedBridgedReserveLocationForConcreteAsset< + UniversalLocation, + BridgedReserves, + >; +} + +#[cfg(feature = "runtime-benchmarks")] +use pallet_bridge_transfer_primitives::{MaybePaidLocation, ReachableDestination}; + +/// Benchmarks helper for over-bridge transfer pallet. +#[cfg(feature = "runtime-benchmarks")] +pub struct BridgeTransferBenchmarksHelper; +#[cfg(feature = "runtime-benchmarks")] +impl pallet_bridge_transfer::BenchmarkHelper for BridgeTransferBenchmarksHelper { + fn desired_bridged_location() -> Option<(NetworkId, ReachableDestination)> { + let bridged_network = bridging::PolkadotNetwork::get(); + let target_location = bridging::AssetHubPolkadot::get(); + let target_location_fee = bridging::AssetHubPolkadotMaxFee::get(); + let target_location_account = target_location + .clone() + .appended_with(AccountId32 { + network: Some(bridged_network), + id: AccountId::from([42u8; 32]).into(), + }) + .expect("Correct target_location_account"); + + Some(( + bridging::PolkadotNetwork::get(), + ReachableDestination { + bridge: MaybePaidLocation { + location: bridging::BridgeHubKusama::get(), + // Right now `UnpaidRemoteExporter` is used to send XCM messages and it requires + // fee to be `None`. If we're going to change that (are we?), then we should replace + // this `None` with `Some(Self::make_asset(crate::ExistentialDeposit::get()))` + maybe_fee: None, + }, + target: MaybePaidLocation { + location: target_location, + maybe_fee: target_location_fee, + }, + target_destination: target_location_account, + }, + )) + } + + fn prepare_asset_transfer_for( + desired_bridged_location: (NetworkId, ReachableDestination), + assumed_reserve_account: MultiLocation, + ) -> Option<(RuntimeOrigin, xcm::VersionedMultiAssets, xcm::VersionedMultiLocation)> { + use frame_support::traits::Currency; + let (_, desired_bridged_location) = desired_bridged_location; + + // our `BridgeXcmSender` assumes that the HRMP channel is opened between this + // parachain and the sibling bridge-hub parachain. + // we expect local bridge-hub + let bridge_hub_para_id = match desired_bridged_location.bridge.location { + MultiLocation { parents: 1, interior: X1(Parachain(bridge_hub_para_id)) } => + bridge_hub_para_id, + _ => panic!("Cannot resolve bridge_hub_para_id"), + }; + cumulus_pallet_parachain_system::Pallet::::open_outbound_hrmp_channel_for_benchmarks( + bridge_hub_para_id.into(), + ); + + // sender account + let sender_account = AccountId::from([42u8; 32]); + // reserve account + use xcm_executor::traits::ConvertLocation; + let assumed_reserve_account = + LocationToAccountId::convert_location(&assumed_reserve_account) + .expect("Correct AccountId"); + + // deposit enough (ED) funds to the sender and reserve account + let existential_deposit = crate::ExistentialDeposit::get(); + let _ = Balances::deposit_creating(&sender_account, existential_deposit * 10); + let _ = Balances::deposit_creating(&assumed_reserve_account, existential_deposit * 10); + + // finally - prepare assets + // lets consider our worst case scenario - reserve based transfer with relay chain tokens + let asset: MultiAsset = (Concrete(KsmLocation::get()), existential_deposit * 2).into(); + + let assets = xcm::VersionedMultiAssets::from(MultiAssets::from(asset)); + let destination = + xcm::VersionedMultiLocation::from(desired_bridged_location.target_destination); + + Some((RuntimeOrigin::signed(sender_account), assets, destination)) + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl BridgeTransferBenchmarksHelper { + pub fn prepare_universal_alias() -> Option<(MultiLocation, Junction)> { + let alias = bridging::BridgedUniversalAliases::get().into_iter().find_map( + |(location, junction)| match bridging::BridgeHubKusama::get().eq(&location) { + true => Some((location, junction)), + false => None, + }, + ); + assert!(alias.is_some(), "we expect here BridgeHubKusama to Polkadot mapping at least"); + Some(alias.unwrap()) + } +} diff --git a/parachains/runtimes/assets/asset-hub-kusama/tests/tests.rs b/parachains/runtimes/assets/asset-hub-kusama/tests/tests.rs index 21cec9bf56c..db2001aa49c 100644 --- a/parachains/runtimes/assets/asset-hub-kusama/tests/tests.rs +++ b/parachains/runtimes/assets/asset-hub-kusama/tests/tests.rs @@ -22,10 +22,13 @@ use asset_hub_kusama_runtime::xcm_config::{ }; pub use asset_hub_kusama_runtime::{ constants::fee::WeightToFee, - xcm_config::{CheckingAccount, ForeignCreatorsSovereignAccountOf, XcmConfig}, + xcm_config::{ + bridging, CheckingAccount, ForeignCreatorsSovereignAccountOf, LocationToAccountId, + XcmConfig, + }, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, Runtime, RuntimeCall, - RuntimeEvent, SessionKeys, System, TrustBackedAssetsInstance, + RuntimeEvent, SessionKeys, System, TrustBackedAssetsInstance, XcmpQueue, }; use asset_test_utils::{CollatorSessionKeys, ExtBuilder, RuntimeHelper}; use codec::{Decode, Encode}; @@ -41,6 +44,7 @@ use xcm::latest::prelude::*; use xcm_executor::traits::{Identity, JustTry, WeightTrader}; const ALICE: [u8; 32] = [1u8; 32]; +const BOB: [u8; 32] = [0u8; 32]; const SOME_ASSET_ADMIN: [u8; 32] = [5u8; 32]; type AssetIdForTrustBackedAssetsConvert = @@ -621,3 +625,142 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p assert_eq!(ForeignAssets::asset_ids().collect::>().len(), 1); }) ); + +fn bridging_to_asset_hub_polkadot() -> asset_test_utils::test_cases_over_bridge::TestBridgingConfig +{ + asset_test_utils::test_cases_over_bridge::TestBridgingConfig { + bridged_network: bridging::PolkadotNetwork::get(), + local_bridge_hub_para_id: bridging::BridgeHubKusamaParaId::get(), + local_bridge_hub_location: pallet_bridge_transfer_primitives::MaybePaidLocation { + location: bridging::BridgeHubKusama::get(), + maybe_fee: None, + }, + bridged_target_location: pallet_bridge_transfer_primitives::MaybePaidLocation { + location: bridging::AssetHubPolkadot::get(), + maybe_fee: bridging::AssetHubPolkadotMaxFee::get(), + }, + } +} + +#[test] +fn transfer_asset_via_bridge_initiate_reserve_based_for_native_asset_works() { + asset_test_utils::test_cases_over_bridge::transfer_asset_via_bridge_initiate_reserve_based_for_native_asset_works::< + Runtime, + XcmConfig, + ParachainSystem, + XcmpQueue, + LocationToAccountId, + >( + collator_session_keys(), + ExistentialDeposit::get(), + AccountId::from(ALICE), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::BridgeTransfer(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + bridging_to_asset_hub_polkadot + ) +} + +#[test] +fn transfer_asset_via_bridge_initiate_withdraw_reserve_for_native_asset_works() { + asset_test_utils::test_cases_over_bridge::transfer_asset_via_bridge_initiate_withdraw_reserve_for_native_asset_works::< + Runtime, + XcmConfig, + ParachainSystem, + XcmpQueue, + LocationToAccountId, + ForeignAssetsInstance, + >( + collator_session_keys(), + ExistentialDeposit::get(), + AccountId::from(ALICE), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::BridgeTransfer(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + bridging_to_asset_hub_polkadot + ) +} + +#[test] +fn receive_reserve_asset_deposited_from_different_consensus_over_bridge_works() { + asset_test_utils::test_cases_over_bridge::receive_reserve_asset_deposited_from_different_consensus_over_bridge_works::< + Runtime, + XcmConfig, + LocationToAccountId, + ForeignAssetsInstance, + >( + collator_session_keys(), + ExistentialDeposit::get(), + AccountId::from(BOB), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + bridging_to_asset_hub_polkadot + ) +} + +#[test] +fn withdraw_reserve_asset_deposited_from_different_consensus_over_bridge_works() { + asset_test_utils::test_cases_over_bridge::withdraw_reserve_asset_deposited_from_different_consensus_over_bridge_works::< + Runtime, + XcmConfig, + LocationToAccountId, + >( + collator_session_keys(), + ExistentialDeposit::get(), + AccountId::from(BOB), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + bridging_to_asset_hub_polkadot + ) +} + +#[test] +fn change_asset_hub_polkadot_max_fee_by_governance_works() { + asset_test_utils::test_cases::change_storage_constant_by_governance_works::< + Runtime, + bridging::AssetHubPolkadotMaxFee, + Option, + >( + collator_session_keys(), + 1000, + Box::new(|call| RuntimeCall::System(call).encode()), + || { + ( + bridging::AssetHubPolkadotMaxFee::key().to_vec(), + bridging::AssetHubPolkadotMaxFee::get(), + ) + }, + |old_value| match old_value { + Some(MultiAsset { id, fun: Fungible(old_amount) }) => + Some(MultiAsset { id: *id, fun: Fungible(old_amount * 2) }), + Some(_) => None, + None => Some(MultiAsset::from((Here, 123456))), + }, + ) +} diff --git a/parachains/runtimes/assets/asset-hub-polkadot/Cargo.toml b/parachains/runtimes/assets/asset-hub-polkadot/Cargo.toml index 0507c7b6e9c..cdc5b205e7a 100644 --- a/parachains/runtimes/assets/asset-hub-polkadot/Cargo.toml +++ b/parachains/runtimes/assets/asset-hub-polkadot/Cargo.toml @@ -73,6 +73,8 @@ pallet-collator-selection = { path = "../../../../pallets/collator-selection", d parachain-info = { path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } assets-common = { path = "../common", default-features = false } +pallet-bridge-transfer = { path = "../../../pallets/bridge-transfer", default-features = false } +pallet-bridge-transfer-primitives = { path = "../../../pallets/bridge-transfer/primitives", default-features = false } [dev-dependencies] hex-literal = "0.4.1" @@ -101,10 +103,12 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "cumulus-pallet-session-benchmarking/runtime-benchmarks", + "cumulus-pallet-parachain-system/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "assets-common/runtime-benchmarks", + "pallet-bridge-transfer/runtime-benchmarks", ] try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", @@ -131,6 +135,7 @@ try-runtime = [ "pallet-utility/try-runtime", "pallet-xcm/try-runtime", "parachain-info/try-runtime", + "pallet-bridge-transfer/try-runtime", ] std = [ "codec/std", @@ -186,5 +191,7 @@ std = [ "parachain-info/std", "parachains-common/std", "assets-common/std", + "pallet-bridge-transfer/std", + "pallet-bridge-transfer-primitives/std", "substrate-wasm-builder", ] diff --git a/parachains/runtimes/assets/asset-hub-polkadot/src/lib.rs b/parachains/runtimes/assets/asset-hub-polkadot/src/lib.rs index e51fe0fab57..9135036c288 100644 --- a/parachains/runtimes/assets/asset-hub-polkadot/src/lib.rs +++ b/parachains/runtimes/assets/asset-hub-polkadot/src/lib.rs @@ -107,8 +107,9 @@ use parachains_common::{ MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, }; use xcm_config::{ - DotLocation, FellowshipLocation, ForeignAssetsConvertedConcreteId, GovernanceLocation, - TrustBackedAssetsConvertedConcreteId, XcmConfig, XcmOriginToTransactDispatchOrigin, + bridging, AssetTransactors, DotLocation, FellowshipLocation, ForeignAssetsConvertedConcreteId, + GovernanceLocation, LocalOriginToLocation, TrustBackedAssetsConvertedConcreteId, + UniversalLocation, XcmConfig, XcmOriginToTransactDispatchOrigin, }; #[cfg(any(feature = "std", test))] @@ -118,6 +119,7 @@ pub use sp_runtime::BuildStorage; use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use xcm::latest::BodyId; +use xcm_builder::EnsureXcmOrigin; use xcm_executor::XcmExecutor; use crate::xcm_config::ForeignCreatorsSovereignAccountOf; @@ -559,7 +561,6 @@ parameter_types! { } impl cumulus_pallet_xcmp_queue::Config for Runtime { - type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; type ChannelInfo = ParachainSystem; @@ -571,6 +572,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { >; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type PriceForSiblingDelivery = (); + type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; } impl cumulus_pallet_dmp_queue::Config for Runtime { @@ -720,6 +722,25 @@ impl pallet_nfts::Config for Runtime { type Helper = (); } +impl pallet_bridge_transfer::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; + type WeightInfo = weights::pallet_bridge_transfer::WeightInfo; + type AssetTransactor = AssetTransactors; + type AssetTransferKindResolver = + pallet_bridge_transfer::features::ConcreteAssetTransferKindResolver< + bridging::IsTrustedBridgedReserveLocationForConcreteAsset, + pallet_bridge_transfer::features::IsAllowedReserveBasedTransferForConcreteAssetToBridgedLocation, + >; + type AssetTransferOrigin = EnsureXcmOrigin; + type AssetsLimit = ConstU8<1>; + type BridgedDestinationValidator = + pallet_bridge_transfer_primitives::BridgesConfigAdapter; + type BridgeXcmSender = bridging::BridgeXcmSender; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = xcm_config::BridgeTransferBenchmarksHelper; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime where @@ -764,6 +785,7 @@ construct_runtime!( Uniques: pallet_uniques::{Pallet, Call, Storage, Event} = 51, Nfts: pallet_nfts::{Pallet, Call, Storage, Event} = 52, ForeignAssets: pallet_assets::::{Pallet, Call, Storage, Event} = 53, + BridgeTransfer: pallet_bridge_transfer::{Pallet, Call, Storage, Event} = 54, } ); @@ -824,6 +846,7 @@ mod benches { [pallet_timestamp, Timestamp] [pallet_collator_selection, CollatorSelection] [cumulus_pallet_xcmp_queue, XcmpQueue] + [pallet_bridge_transfer, BridgeTransfer] // XCM [pallet_xcm, PolkadotXcm] // NOTE: Make sure you point to the individual modules below. @@ -1136,7 +1159,10 @@ impl_runtime_apis! { } fn universal_alias() -> Result<(MultiLocation, Junction), BenchmarkError> { - Err(BenchmarkError::Skip) + match xcm_config::BridgeTransferBenchmarksHelper::prepare_universal_alias() { + Some(alias) => Ok(alias), + None => Err(BenchmarkError::Skip) + } } fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> { diff --git a/parachains/runtimes/assets/asset-hub-polkadot/src/weights/mod.rs b/parachains/runtimes/assets/asset-hub-polkadot/src/weights/mod.rs index 92af360ced1..518bd88bc2e 100644 --- a/parachains/runtimes/assets/asset-hub-polkadot/src/weights/mod.rs +++ b/parachains/runtimes/assets/asset-hub-polkadot/src/weights/mod.rs @@ -4,6 +4,7 @@ pub mod extrinsic_weights; pub mod frame_system; pub mod pallet_assets; pub mod pallet_balances; +pub mod pallet_bridge_transfer; pub mod pallet_collator_selection; pub mod pallet_multisig; pub mod pallet_nfts; diff --git a/parachains/runtimes/assets/asset-hub-polkadot/src/weights/pallet_bridge_transfer.rs b/parachains/runtimes/assets/asset-hub-polkadot/src/weights/pallet_bridge_transfer.rs new file mode 100644 index 00000000000..0341dec67c9 --- /dev/null +++ b/parachains/runtimes/assets/asset-hub-polkadot/src/weights/pallet_bridge_transfer.rs @@ -0,0 +1,82 @@ +// Copyright 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 . + +//! Autogenerated weights for `pallet_bridge_transfer` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-06-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-polkadot-dev"), DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_bridge_transfer +// --chain=asset-hub-polkadot-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/assets/asset-hub-polkadot/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_bridge_transfer`. +pub struct WeightInfo(PhantomData); +impl pallet_bridge_transfer::WeightInfo for WeightInfo { + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: unknown `0x732f1cab5e78eadace5c4f5d259acee9` (r:1 w:0) + /// Proof Skipped: unknown `0x732f1cab5e78eadace5c4f5d259acee9` (r:1 w:0) + /// Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + /// Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: PolkadotXcm SupportedVersion (r:1 w:0) + /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + /// Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + /// Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainSystem RelevantMessagingState (r:1 w:0) + /// Proof Skipped: ParachainSystem RelevantMessagingState (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: XcmpQueue OutboundXcmpStatus (r:1 w:1) + /// Proof Skipped: XcmpQueue OutboundXcmpStatus (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: XcmpQueue OutboundXcmpMessages (r:0 w:1) + /// Proof Skipped: XcmpQueue OutboundXcmpMessages (max_values: None, max_size: None, mode: Measured) + fn transfer_asset_via_bridge() -> Weight { + // Proof Size summary in bytes: + // Measured: `520` + // Estimated: `6196` + // Minimum execution time: 155_232_000 picoseconds. + Weight::from_parts(159_180_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(10)) + .saturating_add(T::DbWeight::get().writes(6)) + } +} diff --git a/parachains/runtimes/assets/asset-hub-polkadot/src/weights/xcm/mod.rs b/parachains/runtimes/assets/asset-hub-polkadot/src/weights/xcm/mod.rs index 234ab204fbc..69c138c888a 100644 --- a/parachains/runtimes/assets/asset-hub-polkadot/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/asset-hub-polkadot/src/weights/xcm/mod.rs @@ -214,7 +214,7 @@ impl XcmWeightInfo for AssetHubPolkadotXcmWeight { XcmGeneric::::clear_transact_status() } fn universal_origin(_: &Junction) -> Weight { - Weight::MAX + XcmGeneric::::universal_origin() } fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight { Weight::MAX diff --git a/parachains/runtimes/assets/asset-hub-polkadot/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/asset-hub-polkadot/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 16bb74621ad..a7ba2221693 100644 --- a/parachains/runtimes/assets/asset-hub-polkadot/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/asset-hub-polkadot/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,26 +17,27 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-06-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm6`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-polkadot-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=asset-hub-polkadot-dev +// --steps=50 +// --repeat=20 +// --extrinsic=* // --execution=wasm // --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::generic -// --extrinsic=* -// --steps=50 -// --repeat=20 -// --json +// --chain=asset-hub-polkadot-dev // --header=./file_header.txt -// --output=./parachains/runtimes/assets/asset-hub-polkadot/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +// --template=./templates/xcm-bench-template.hbs +// --output=./parachains/runtimes/assets/asset-hub-polkadot/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,6 +51,8 @@ pub struct WeightInfo(PhantomData); impl WeightInfo { // Storage: ParachainInfo ParachainId (r:1 w:0) // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -64,17 +67,17 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `75` // Estimated: `3540` - // Minimum execution time: 342_499_000 picoseconds. - Weight::from_parts(348_390_000, 3540) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) + // Minimum execution time: 348_938_000 picoseconds. + Weight::from_parts(350_779_000, 3540) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) } pub fn buy_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_768_000 picoseconds. - Weight::from_parts(3_863_000, 0) + // Minimum execution time: 3_749_000 picoseconds. + Weight::from_parts(3_919_000, 0) } // Storage: PolkadotXcm Queries (r:1 w:0) // Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) @@ -82,61 +85,63 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `69` // Estimated: `3534` - // Minimum execution time: 10_749_000 picoseconds. - Weight::from_parts(11_052_000, 3534) + // Minimum execution time: 10_776_000 picoseconds. + Weight::from_parts(11_028_000, 3534) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 13_123_000 picoseconds. - Weight::from_parts(13_525_000, 0) + // Minimum execution time: 13_250_000 picoseconds. + Weight::from_parts(13_476_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_117_000 picoseconds. - Weight::from_parts(4_237_000, 0) + // Minimum execution time: 4_072_000 picoseconds. + Weight::from_parts(4_131_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_547_000 picoseconds. - Weight::from_parts(2_632_000, 0) + // Minimum execution time: 2_734_000 picoseconds. + Weight::from_parts(2_801_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_644_000 picoseconds. - Weight::from_parts(2_735_000, 0) + // Minimum execution time: 2_658_000 picoseconds. + Weight::from_parts(2_734_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_600_000 picoseconds. - Weight::from_parts(2_656_000, 0) + // Minimum execution time: 2_617_000 picoseconds. + Weight::from_parts(2_688_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_404_000 picoseconds. - Weight::from_parts(3_493_000, 0) + // Minimum execution time: 3_621_000 picoseconds. + Weight::from_parts(3_703_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_611_000 picoseconds. - Weight::from_parts(2_689_000, 0) + // Minimum execution time: 2_571_000 picoseconds. + Weight::from_parts(2_662_000, 0) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -151,10 +156,10 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `75` // Estimated: `3540` - // Minimum execution time: 24_740_000 picoseconds. - Weight::from_parts(25_350_000, 3540) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) + // Minimum execution time: 27_767_000 picoseconds. + Weight::from_parts(28_324_000, 3540) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) } // Storage: PolkadotXcm AssetTraps (r:1 w:1) // Proof Skipped: PolkadotXcm AssetTraps (max_values: None, max_size: None, mode: Measured) @@ -162,8 +167,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `126` // Estimated: `3591` - // Minimum execution time: 15_693_000 picoseconds. - Weight::from_parts(16_027_000, 3591) + // Minimum execution time: 15_855_000 picoseconds. + Weight::from_parts(16_046_000, 3591) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -171,11 +176,13 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_626_000 picoseconds. - Weight::from_parts(2_696_000, 0) + // Minimum execution time: 2_572_000 picoseconds. + Weight::from_parts(2_676_000, 0) } // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -190,10 +197,10 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `75` // Estimated: `3540` - // Minimum execution time: 28_000_000 picoseconds. - Weight::from_parts(28_307_000, 3540) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) + // Minimum execution time: 30_238_000 picoseconds. + Weight::from_parts(30_909_000, 3540) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(4)) } // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) @@ -201,12 +208,14 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_996_000 picoseconds. - Weight::from_parts(5_058_000, 0) + // Minimum execution time: 4_836_000 picoseconds. + Weight::from_parts(4_966_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -221,48 +230,50 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `75` // Estimated: `3540` - // Minimum execution time: 386_102_000 picoseconds. - Weight::from_parts(389_687_000, 3540) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) + // Minimum execution time: 389_344_000 picoseconds. + Weight::from_parts(391_677_000, 3540) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 117_812_000 picoseconds. - Weight::from_parts(120_875_000, 0) + // Minimum execution time: 119_600_000 picoseconds. + Weight::from_parts(120_116_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_499_000 picoseconds. - Weight::from_parts(12_659_000, 0) + // Minimum execution time: 11_966_000 picoseconds. + Weight::from_parts(12_237_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_656_000 picoseconds. - Weight::from_parts(2_763_000, 0) + // Minimum execution time: 2_730_000 picoseconds. + Weight::from_parts(2_820_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_613_000 picoseconds. + // Minimum execution time: 2_607_000 picoseconds. Weight::from_parts(2_700_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_814_000 picoseconds. - Weight::from_parts(2_931_000, 0) + // Minimum execution time: 2_752_000 picoseconds. + Weight::from_parts(2_867_000, 0) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -277,20 +288,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `75` // Estimated: `3540` - // Minimum execution time: 28_529_000 picoseconds. - Weight::from_parts(29_029_000, 3540) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) + // Minimum execution time: 31_044_000 picoseconds. + Weight::from_parts(31_788_000, 3540) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) } pub fn expect_pallet() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_108_000 picoseconds. - Weight::from_parts(5_185_000, 0) + // Minimum execution time: 5_150_000 picoseconds. + Weight::from_parts(5_206_000, 0) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -305,44 +318,54 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `75` // Estimated: `3540` - // Minimum execution time: 25_014_000 picoseconds. - Weight::from_parts(25_814_000, 3540) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) + // Minimum execution time: 28_127_000 picoseconds. + Weight::from_parts(28_578_000, 3540) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) } pub fn clear_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_618_000 picoseconds. - Weight::from_parts(2_781_000, 0) + // Minimum execution time: 2_619_000 picoseconds. + Weight::from_parts(2_697_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_585_000 picoseconds. - Weight::from_parts(2_676_000, 0) + // Minimum execution time: 2_586_000 picoseconds. + Weight::from_parts(2_672_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_597_000 picoseconds. - Weight::from_parts(2_675_000, 0) + // Minimum execution time: 2_596_000 picoseconds. + Weight::from_parts(2_654_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_113_000 picoseconds. + Weight::from_parts(5_209_000, 1489) + .saturating_add(T::DbWeight::get().reads(1)) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_502_000 picoseconds. - Weight::from_parts(2_569_000, 0) + // Minimum execution time: 2_597_000 picoseconds. + Weight::from_parts(2_698_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_807_000 picoseconds. - Weight::from_parts(2_878_000, 0) + // Minimum execution time: 2_753_000 picoseconds. + Weight::from_parts(2_850_000, 0) } } diff --git a/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs b/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs index 956ada5ac3b..11d7012e1f9 100644 --- a/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs @@ -35,11 +35,11 @@ use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, DenyReserveTransferToRelayChain, DenyThenTry, DescribeFamily, DescribePalletTerminal, - EnsureXcmOrigin, FungiblesAdapter, HashedDescription, IsConcrete, LocalMint, NativeAsset, - NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, - WithComputedOrigin, + EnsureXcmOrigin, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, + IsConcrete, LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -70,6 +70,9 @@ pub type LocationToAccountId = ( // Foreign chain account alias into local accounts according to a hash of their standard // description. HashedDescription>, + // Different global consensus parachain sovereign account. + // (Used for over-bridge transfers and reserve processing) + GlobalConsensusParachainConvertsFor, ); /// Means for transacting the native currency on this chain. @@ -203,6 +206,14 @@ impl Contains for SafeCallFilter { } } + // Allow to change dedicated storage items (called by governance-like) + match call { + RuntimeCall::System(frame_system::Call::set_storage { items }) + if items.iter().any(|(k, _)| k.eq(&bridging::AssetHubKusamaMaxFee::key())) => + return true, + _ => (), + }; + matches!( call, RuntimeCall::PolkadotXcm(pallet_xcm::Call::force_xcm_version { .. }) | @@ -350,30 +361,32 @@ impl Contains for SafeCallFilter { } } -pub type Barrier = DenyThenTry< - DenyReserveTransferToRelayChain, - ( - TakeWeightCredit, - // Expected responses are OK. - AllowKnownQueryResponses, - // Allow XCMs with some computed origins to pass through. - WithComputedOrigin< - ( - // If the message is one that immediately attemps to pay for execution, then allow it. - AllowTopLevelPaidExecutionFrom, - // Parent, its pluralities (i.e. governance bodies), and the Fellows plurality get free execution. - AllowExplicitUnpaidExecutionFrom<( - ParentOrParentsPlurality, - FellowsPlurality, - FellowshipSalaryPallet, - )>, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, - ), - UniversalLocation, - ConstU32<8>, - >, - ), +pub type Barrier = TrailingSetTopicAsId< + DenyThenTry< + DenyReserveTransferToRelayChain, + ( + TakeWeightCredit, + // Expected responses are OK. + AllowKnownQueryResponses, + // Allow XCMs with some computed origins to pass through. + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Parent, its pluralities (i.e. governance bodies), and the Fellows plurality get free execution. + AllowExplicitUnpaidExecutionFrom<( + ParentOrParentsPlurality, + FellowsPlurality, + FellowshipSalaryPallet, + )>, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, + ), + >, >; pub type AssetFeeAsExistentialDepositMultiplierFeeCharger = AssetFeeAsExistentialDepositMultiplier< @@ -389,10 +402,9 @@ impl xcm_executor::Config for XcmConfig { type XcmSender = XcmRouter; type AssetTransactor = AssetTransactors; type OriginConverter = XcmOriginToTransactDispatchOrigin; - // Asset Hub Polkadot does not recognize a reserve location for any asset. This does not prevent // Asset Hub acting _as_ a reserve location for DOT and assets created under `pallet-assets`. // For DOT, users must use teleport where allowed (e.g. with the Relay Chain). - type IsReserve = (); + type IsReserve = bridging::IsTrustedBridgedReserveLocationForConcreteAsset; // We allow: // - teleportation of DOT // - teleportation of sibling parachain's assets (as ForeignCreators) @@ -431,7 +443,7 @@ impl xcm_executor::Config for XcmConfig { type AssetExchanger = (); type FeeManager = (); type MessageExporter = (); - type UniversalAliases = Nothing; + type UniversalAliases = bridging::BridgedUniversalAliases; type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; @@ -443,12 +455,12 @@ pub type LocalOriginToLocation = SignedToAccountId32, // ..and XCMP to communicate with the sibling chains. XcmpQueue, -); +)>; #[cfg(feature = "runtime-benchmarks")] parameter_types! { @@ -524,3 +536,185 @@ fn foreign_pallet_has_correct_local_account() { let address = Ss58Codec::to_ss58check_with_version(&account, polkadot); assert_eq!(address, "13w7NdvSR1Af8xsQTArDtZmVvjE8XhWNdL4yed3iFHrUNCnS"); } + +/// All configuration related to bridging +pub mod bridging { + use super::*; + use pallet_bridge_transfer_primitives::{ + AssetFilter, BridgeConfig, BridgesConfig, BridgesConfigAdapter, BridgesConfigBuilder, + MaybePaidLocation, MultiLocationFilter, ReserveLocation, + }; + use sp_std::collections::btree_set::BTreeSet; + use xcm_builder::UnpaidRemoteExporter; + + parameter_types! { + pub BridgeHubPolkadotParaId: u32 = 1002; + pub BridgeHubPolkadot: MultiLocation = MultiLocation::new(1, X1(Parachain(BridgeHubPolkadotParaId::get()))); + pub const KusamaNetwork: NetworkId = NetworkId::Kusama; + pub AssetHubKusama: MultiLocation = MultiLocation::new(2, X2(GlobalConsensus(KusamaNetwork::get()), Parachain(1000))); + // Initial value, this will be adjusted by governance motion on deployment with some more accurate value + pub storage AssetHubKusamaMaxFee: Option = Some((MultiLocation::parent(), 1_000_000).into()); + pub KsmLocation: MultiLocation = MultiLocation::new(2, X1(GlobalConsensus(KusamaNetwork::get()))); + + // Setup bridges configuration + // (hard-coded version - on-chain configuration will come later as separate feature) + pub Bridges: BridgesConfig = BridgesConfigBuilder::default() + // add exporter for Kusama + .add_or_panic( + KusamaNetwork::get(), + BridgeConfig::new( + MaybePaidLocation { + location: BridgeHubPolkadot::get(), + // Noe fees needed because we use `UnpaidRemoteExporter` and BridgeHubPolkadot allows unpaid execution for local system parachains + maybe_fee: None, + } + ).add_target_location( + // add target location as AssetHubPolkadot + MaybePaidLocation { + location: AssetHubKusama::get(), + maybe_fee: AssetHubKusamaMaxFee::get(), + }, + Some(AssetFilter::ByMultiLocation( + MultiLocationFilter::default() + // allow transfer DOT + .add_equals(DotLocation::get()) + )) + ) + ) + .build(); + + // Setup trusted bridged reserve locations + pub BridgedReserves: sp_std::vec::Vec = sp_std::vec![ + // trust assets from AssetHubPolkadot + ( + AssetHubKusama::get(), + AssetFilter::ByMultiLocation( + MultiLocationFilter::default() + // allow receive KSM + .add_equals(KsmLocation::get()) + ) + ) + ]; + + /// Universal aliases + pub BridgedUniversalAliases: BTreeSet<(MultiLocation, Junction)> = BTreeSet::from_iter( + sp_std::vec![ + (BridgeHubPolkadot::get(), GlobalConsensus(KusamaNetwork::get())) + ] + ); + } + + impl Contains<(MultiLocation, Junction)> for BridgedUniversalAliases { + fn contains(alias: &(MultiLocation, Junction)) -> bool { + BridgedUniversalAliases::get().contains(alias) + } + } + + /// Bridge router, which wraps and sends xcm to BridgeHub to be delivered to the different GlobalConsensus + pub type BridgeXcmSender = + UnpaidRemoteExporter, XcmRouter, UniversalLocation>; + + /// Reserve locations filter for `xcm_executor::Config::IsReserve`. + pub type IsTrustedBridgedReserveLocationForConcreteAsset = + pallet_bridge_transfer::features::IsTrustedBridgedReserveLocationForConcreteAsset< + UniversalLocation, + BridgedReserves, + >; +} + +#[cfg(feature = "runtime-benchmarks")] +use pallet_bridge_transfer_primitives::{MaybePaidLocation, ReachableDestination}; + +/// Benchmarks helper for over-bridge transfer pallet. +#[cfg(feature = "runtime-benchmarks")] +pub struct BridgeTransferBenchmarksHelper; +#[cfg(feature = "runtime-benchmarks")] +impl pallet_bridge_transfer::BenchmarkHelper for BridgeTransferBenchmarksHelper { + fn desired_bridged_location() -> Option<(NetworkId, ReachableDestination)> { + let bridged_network = bridging::KusamaNetwork::get(); + let target_location = bridging::AssetHubKusama::get(); + let target_location_fee = bridging::AssetHubKusamaMaxFee::get(); + let target_location_account = target_location + .clone() + .appended_with(AccountId32 { + network: Some(bridged_network), + id: AccountId::from([42u8; 32]).into(), + }) + .expect("Correct target_location_account"); + + Some(( + bridging::KusamaNetwork::get(), + ReachableDestination { + bridge: MaybePaidLocation { + location: bridging::BridgeHubPolkadot::get(), + // Right now `UnpaidRemoteExporter` is used to send XCM messages and it requires + // fee to be `None`. If we're going to change that (are we?), then we should replace + // this `None` with `Some(Self::make_asset(crate::ExistentialDeposit::get()))` + maybe_fee: None, + }, + target: MaybePaidLocation { + location: target_location, + maybe_fee: target_location_fee, + }, + target_destination: target_location_account, + }, + )) + } + + fn prepare_asset_transfer_for( + desired_bridged_location: (NetworkId, ReachableDestination), + assumed_reserve_account: MultiLocation, + ) -> Option<(RuntimeOrigin, xcm::VersionedMultiAssets, xcm::VersionedMultiLocation)> { + use frame_support::traits::Currency; + let (_, desired_bridged_location) = desired_bridged_location; + + // our `BridgeXcmSender` assumes that the HRMP channel is opened between this + // parachain and the sibling bridge-hub parachain. + // we expect local bridge-hub + let bridge_hub_para_id = match desired_bridged_location.bridge.location { + MultiLocation { parents: 1, interior: X1(Parachain(bridge_hub_para_id)) } => + bridge_hub_para_id, + _ => panic!("Cannot resolve bridge_hub_para_id"), + }; + cumulus_pallet_parachain_system::Pallet::::open_outbound_hrmp_channel_for_benchmarks( + bridge_hub_para_id.into(), + ); + + // sender account + let sender_account = AccountId::from([42u8; 32]); + // reserve account + use xcm_executor::traits::ConvertLocation; + let assumed_reserve_account = + LocationToAccountId::convert_location(&assumed_reserve_account) + .expect("Correct AccountId"); + + // deposit enough (ED) funds to the sender and reserve account + let existential_deposit = crate::ExistentialDeposit::get(); + let _ = Balances::deposit_creating(&sender_account, existential_deposit * 10); + let _ = Balances::deposit_creating(&assumed_reserve_account, existential_deposit * 10); + + // finally - prepare assets + // lets consider our worst case scenario - reserve based transfer with relay chain tokens + let asset: MultiAsset = (Concrete(DotLocation::get()), existential_deposit * 2).into(); + + let assets = xcm::VersionedMultiAssets::from(MultiAssets::from(asset)); + let destination = + xcm::VersionedMultiLocation::from(desired_bridged_location.target_destination); + + Some((RuntimeOrigin::signed(sender_account), assets, destination)) + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl BridgeTransferBenchmarksHelper { + pub fn prepare_universal_alias() -> Option<(MultiLocation, Junction)> { + let alias = bridging::BridgedUniversalAliases::get().into_iter().find_map( + |(location, junction)| match bridging::BridgeHubPolkadot::get().eq(&location) { + true => Some((location, junction)), + false => None, + }, + ); + assert!(alias.is_some(), "we expect here BridgeHubPolkadot to Kusama mapping at least"); + Some(alias.unwrap()) + } +} diff --git a/parachains/runtimes/assets/asset-hub-polkadot/tests/tests.rs b/parachains/runtimes/assets/asset-hub-polkadot/tests/tests.rs index cdd61f26796..d67091ec0c9 100644 --- a/parachains/runtimes/assets/asset-hub-polkadot/tests/tests.rs +++ b/parachains/runtimes/assets/asset-hub-polkadot/tests/tests.rs @@ -18,13 +18,14 @@ //! Tests for the Statemint (Polkadot Assets Hub) chain. use asset_hub_polkadot_runtime::xcm_config::{ - AssetFeeAsExistentialDepositMultiplierFeeCharger, CheckingAccount, DotLocation, - ForeignCreatorsSovereignAccountOf, TrustBackedAssetsPalletLocation, XcmConfig, + bridging, AssetFeeAsExistentialDepositMultiplierFeeCharger, CheckingAccount, DotLocation, + ForeignCreatorsSovereignAccountOf, LocationToAccountId, TrustBackedAssetsPalletLocation, + XcmConfig, }; pub use asset_hub_polkadot_runtime::{ constants::fee::WeightToFee, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, Runtime, - RuntimeCall, RuntimeEvent, SessionKeys, System, TrustBackedAssetsInstance, + RuntimeCall, RuntimeEvent, SessionKeys, System, TrustBackedAssetsInstance, XcmpQueue, }; use asset_test_utils::{CollatorSessionKeys, ExtBuilder, RuntimeHelper}; use codec::{Decode, Encode}; @@ -42,6 +43,7 @@ use xcm::latest::prelude::*; use xcm_executor::traits::{Identity, JustTry, WeightTrader}; const ALICE: [u8; 32] = [1u8; 32]; +const BOB: [u8; 32] = [0u8; 32]; const SOME_ASSET_ADMIN: [u8; 32] = [5u8; 32]; type AssetIdForTrustBackedAssetsConvert = @@ -646,3 +648,136 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p assert_eq!(ForeignAssets::asset_ids().collect::>().len(), 1); }) ); + +fn bridging_to_asset_hub_kusama() -> asset_test_utils::test_cases_over_bridge::TestBridgingConfig { + asset_test_utils::test_cases_over_bridge::TestBridgingConfig { + bridged_network: bridging::KusamaNetwork::get(), + local_bridge_hub_para_id: bridging::BridgeHubPolkadotParaId::get(), + local_bridge_hub_location: pallet_bridge_transfer_primitives::MaybePaidLocation { + location: bridging::BridgeHubPolkadot::get(), + maybe_fee: None, + }, + bridged_target_location: pallet_bridge_transfer_primitives::MaybePaidLocation { + location: bridging::AssetHubKusama::get(), + maybe_fee: bridging::AssetHubKusamaMaxFee::get(), + }, + } +} + +#[test] +fn transfer_asset_via_bridge_initiate_reserve_based_for_native_asset_works() { + asset_test_utils::test_cases_over_bridge::transfer_asset_via_bridge_initiate_reserve_based_for_native_asset_works::< + Runtime, + XcmConfig, + ParachainSystem, + XcmpQueue, + LocationToAccountId, + >( + collator_session_keys(), + ExistentialDeposit::get(), + AccountId::from(ALICE), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::BridgeTransfer(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + bridging_to_asset_hub_kusama + ) +} + +#[test] +fn transfer_asset_via_bridge_initiate_withdraw_reserve_for_native_asset_works() { + asset_test_utils::test_cases_over_bridge::transfer_asset_via_bridge_initiate_withdraw_reserve_for_native_asset_works::< + Runtime, + XcmConfig, + ParachainSystem, + XcmpQueue, + LocationToAccountId, + ForeignAssetsInstance, + >( + collator_session_keys(), + ExistentialDeposit::get(), + AccountId::from(ALICE), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::BridgeTransfer(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + bridging_to_asset_hub_kusama + ) +} + +#[test] +fn receive_reserve_asset_deposited_from_different_consensus_over_bridge_works() { + asset_test_utils::test_cases_over_bridge::receive_reserve_asset_deposited_from_different_consensus_over_bridge_works::< + Runtime, + XcmConfig, + LocationToAccountId, + ForeignAssetsInstance, + >( + collator_session_keys(), + ExistentialDeposit::get(), + AccountId::from(BOB), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + bridging_to_asset_hub_kusama + ) +} + +#[test] +fn withdraw_reserve_asset_deposited_from_different_consensus_over_bridge_works() { + asset_test_utils::test_cases_over_bridge::withdraw_reserve_asset_deposited_from_different_consensus_over_bridge_works::< + Runtime, + XcmConfig, + LocationToAccountId, + >( + collator_session_keys(), + ExistentialDeposit::get(), + AccountId::from(BOB), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + bridging_to_asset_hub_kusama + ) +} + +#[test] +fn change_asset_hub_kusama_max_fee_by_governance_works() { + asset_test_utils::test_cases::change_storage_constant_by_governance_works::< + Runtime, + bridging::AssetHubKusamaMaxFee, + Option, + >( + collator_session_keys(), + 1000, + Box::new(|call| RuntimeCall::System(call).encode()), + || (bridging::AssetHubKusamaMaxFee::key().to_vec(), bridging::AssetHubKusamaMaxFee::get()), + |old_value| match old_value { + Some(MultiAsset { id, fun: Fungible(old_amount) }) => + Some(MultiAsset { id: *id, fun: Fungible(old_amount * 2) }), + Some(_) => None, + None => Some(MultiAsset::from((Here, 123456))), + }, + ) +} diff --git a/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index 40b3b25e681..94085acbef4 100644 --- a/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -73,6 +73,8 @@ pallet-collator-selection = { path = "../../../../pallets/collator-selection", d parachain-info = { path = "../../../pallets/parachain-info", default-features = false } parachains-common = { path = "../../../common", default-features = false } assets-common = { path = "../common", default-features = false } +pallet-bridge-transfer = { path = "../../../pallets/bridge-transfer", default-features = false } +pallet-bridge-transfer-primitives = { path = "../../../pallets/bridge-transfer/primitives", default-features = false } [dev-dependencies] hex-literal = "0.4.1" @@ -102,10 +104,12 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "cumulus-pallet-session-benchmarking/runtime-benchmarks", + "cumulus-pallet-parachain-system/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "assets-common/runtime-benchmarks", + "pallet-bridge-transfer/runtime-benchmarks", ] try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", @@ -133,6 +137,7 @@ try-runtime = [ "pallet-utility/try-runtime", "pallet-xcm/try-runtime", "parachain-info/try-runtime", + "pallet-bridge-transfer/try-runtime", ] std = [ "codec/std", @@ -189,5 +194,7 @@ std = [ "parachain-info/std", "parachains-common/std", "assets-common/std", + "pallet-bridge-transfer/std", + "pallet-bridge-transfer-primitives/std", "substrate-wasm-builder", ] diff --git a/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index aa28ca5b5ca..13b8f863238 100644 --- a/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -69,8 +69,8 @@ use parachains_common::{ NORMAL_DISPATCH_RATIO, SLOT_DURATION, }; use xcm_config::{ - ForeignAssetsConvertedConcreteId, TrustBackedAssetsConvertedConcreteId, WestendLocation, - XcmConfig, XcmOriginToTransactDispatchOrigin, + bridging, ForeignAssetsConvertedConcreteId, TrustBackedAssetsConvertedConcreteId, + WestendLocation, XcmConfig, XcmOriginToTransactDispatchOrigin, }; #[cfg(any(feature = "std", test))] @@ -80,9 +80,12 @@ use assets_common::{ foreign_creators::ForeignCreators, matching::FromSiblingParachain, MultiLocationForAssetId, }; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; +use xcm_builder::EnsureXcmOrigin; use xcm_executor::XcmExecutor; -use crate::xcm_config::ForeignCreatorsSovereignAccountOf; +use crate::xcm_config::{ + AssetTransactors, ForeignCreatorsSovereignAccountOf, LocalOriginToLocation, UniversalLocation, +}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; impl_opaque_keys! { @@ -696,6 +699,25 @@ impl pallet_nfts::Config for Runtime { type Helper = (); } +impl pallet_bridge_transfer::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; + type WeightInfo = weights::pallet_bridge_transfer::WeightInfo; + type AssetTransactor = AssetTransactors; + type AssetTransferKindResolver = + pallet_bridge_transfer::features::ConcreteAssetTransferKindResolver< + bridging::IsTrustedBridgedReserveLocationForConcreteAsset, + pallet_bridge_transfer::features::IsAllowedReserveBasedTransferForConcreteAssetToBridgedLocation, + >; + type AssetTransferOrigin = EnsureXcmOrigin; + type AssetsLimit = ConstU8<1>; + type BridgedDestinationValidator = + pallet_bridge_transfer_primitives::BridgesConfigAdapter; + type BridgeXcmSender = bridging::BridgeXcmSender; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = xcm_config::BridgeTransferBenchmarksHelper; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime where @@ -741,6 +763,7 @@ construct_runtime!( Nfts: pallet_nfts::{Pallet, Call, Storage, Event} = 52, ForeignAssets: pallet_assets::::{Pallet, Call, Storage, Event} = 53, NftFractionalization: pallet_nft_fractionalization::{Pallet, Call, Storage, Event, HoldReason} = 54, + BridgeTransfer: pallet_bridge_transfer::{Pallet, Call, Storage, Event} = 55, } ); @@ -806,6 +829,7 @@ mod benches { [pallet_timestamp, Timestamp] [pallet_collator_selection, CollatorSelection] [cumulus_pallet_xcmp_queue, XcmpQueue] + [pallet_bridge_transfer, BridgeTransfer] // XCM [pallet_xcm, PolkadotXcm] // NOTE: Make sure you point to the individual modules below. @@ -1162,7 +1186,10 @@ impl_runtime_apis! { } fn universal_alias() -> Result<(MultiLocation, Junction), BenchmarkError> { - Err(BenchmarkError::Skip) + match xcm_config::BridgeTransferBenchmarksHelper::prepare_universal_alias() { + Some(alias) => Ok(alias), + None => Err(BenchmarkError::Skip) + } } fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> { diff --git a/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs b/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs index 0a0188da823..cbebd04da2b 100644 --- a/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs +++ b/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs @@ -4,6 +4,7 @@ pub mod extrinsic_weights; pub mod frame_system; pub mod pallet_assets; pub mod pallet_balances; +pub mod pallet_bridge_transfer; pub mod pallet_collator_selection; pub mod pallet_multisig; pub mod pallet_nft_fractionalization; diff --git a/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_bridge_transfer.rs b/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_bridge_transfer.rs new file mode 100644 index 00000000000..5851ba35a6a --- /dev/null +++ b/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_bridge_transfer.rs @@ -0,0 +1,82 @@ +// Copyright 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 . + +//! Autogenerated weights for `pallet_bridge_transfer` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-06-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-westend-dev"), DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_bridge_transfer +// --chain=asset-hub-westend-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/assets/asset-hub-westend/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_bridge_transfer`. +pub struct WeightInfo(PhantomData); +impl pallet_bridge_transfer::WeightInfo for WeightInfo { + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: unknown `0x36e13a2aa5713f59bab8576f408dc957` (r:1 w:0) + /// Proof Skipped: unknown `0x36e13a2aa5713f59bab8576f408dc957` (r:1 w:0) + /// Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + /// Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: PolkadotXcm SupportedVersion (r:1 w:0) + /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + /// Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + /// Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainSystem RelevantMessagingState (r:1 w:0) + /// Proof Skipped: ParachainSystem RelevantMessagingState (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: XcmpQueue OutboundXcmpStatus (r:1 w:1) + /// Proof Skipped: XcmpQueue OutboundXcmpStatus (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: XcmpQueue OutboundXcmpMessages (r:0 w:1) + /// Proof Skipped: XcmpQueue OutboundXcmpMessages (max_values: None, max_size: None, mode: Measured) + fn transfer_asset_via_bridge() -> Weight { + // Proof Size summary in bytes: + // Measured: `554` + // Estimated: `6196` + // Minimum execution time: 157_282_000 picoseconds. + Weight::from_parts(239_207_000, 0) + .saturating_add(Weight::from_parts(0, 6196)) + .saturating_add(T::DbWeight::get().reads(10)) + .saturating_add(T::DbWeight::get().writes(6)) + } +} diff --git a/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/mod.rs b/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/mod.rs index 4cc46939843..aebbf68cf3c 100644 --- a/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/mod.rs @@ -214,7 +214,7 @@ impl XcmWeightInfo for AssetHubWestendXcmWeight { XcmGeneric::::clear_transact_status() } fn universal_origin(_: &Junction) -> Weight { - Weight::MAX + XcmGeneric::::universal_origin() } fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight { Weight::MAX diff --git a/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 8a7e8f9d7db..5c79d3b2a5a 100644 --- a/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,26 +17,27 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-06-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bm6`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-westend-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=asset-hub-westend-dev +// --steps=50 +// --repeat=20 +// --extrinsic=* // --execution=wasm // --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::generic -// --extrinsic=* -// --steps=50 -// --repeat=20 -// --json +// --chain=asset-hub-westend-dev // --header=./file_header.txt -// --output=./parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +// --template=./templates/xcm-bench-template.hbs +// --output=./parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,6 +51,8 @@ pub struct WeightInfo(PhantomData); impl WeightInfo { // Storage: ParachainInfo ParachainId (r:1 w:0) // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -64,17 +67,17 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 345_759_000 picoseconds. - Weight::from_parts(353_565_000, 3574) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) + // Minimum execution time: 343_461_000 picoseconds. + Weight::from_parts(348_335_000, 3574) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) } pub fn buy_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_727_000 picoseconds. - Weight::from_parts(3_865_000, 0) + // Minimum execution time: 3_681_000 picoseconds. + Weight::from_parts(3_879_000, 0) } // Storage: PolkadotXcm Queries (r:1 w:0) // Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) @@ -82,61 +85,63 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3568` - // Minimum execution time: 10_978_000 picoseconds. - Weight::from_parts(11_169_000, 3568) + // Minimum execution time: 10_554_000 picoseconds. + Weight::from_parts(10_845_000, 3568) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 13_477_000 picoseconds. - Weight::from_parts(13_824_000, 0) + // Minimum execution time: 13_168_000 picoseconds. + Weight::from_parts(13_581_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_993_000 picoseconds. - Weight::from_parts(4_166_000, 0) + // Minimum execution time: 4_046_000 picoseconds. + Weight::from_parts(4_283_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_593_000 picoseconds. - Weight::from_parts(2_711_000, 0) + // Minimum execution time: 2_552_000 picoseconds. + Weight::from_parts(2_630_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_606_000 picoseconds. - Weight::from_parts(2_665_000, 0) + // Minimum execution time: 2_557_000 picoseconds. + Weight::from_parts(2_714_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_564_000 picoseconds. - Weight::from_parts(2_628_000, 0) + // Minimum execution time: 2_509_000 picoseconds. + Weight::from_parts(2_583_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_392_000 picoseconds. - Weight::from_parts(3_489_000, 0) + // Minimum execution time: 3_429_000 picoseconds. + Weight::from_parts(3_609_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_569_000 picoseconds. - Weight::from_parts(2_652_000, 0) + // Minimum execution time: 2_524_000 picoseconds. + Weight::from_parts(2_602_000, 0) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -151,10 +156,10 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 25_378_000 picoseconds. - Weight::from_parts(25_790_000, 3574) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) + // Minimum execution time: 27_356_000 picoseconds. + Weight::from_parts(27_839_000, 3574) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) } // Storage: PolkadotXcm AssetTraps (r:1 w:1) // Proof Skipped: PolkadotXcm AssetTraps (max_values: None, max_size: None, mode: Measured) @@ -162,8 +167,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `160` // Estimated: `3625` - // Minimum execution time: 15_992_000 picoseconds. - Weight::from_parts(16_178_000, 3625) + // Minimum execution time: 15_675_000 picoseconds. + Weight::from_parts(15_995_000, 3625) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -171,11 +176,13 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_600_000 picoseconds. - Weight::from_parts(2_671_000, 0) + // Minimum execution time: 2_542_000 picoseconds. + Weight::from_parts(2_594_000, 0) } // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -190,10 +197,10 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 27_531_000 picoseconds. - Weight::from_parts(28_132_000, 3574) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(3)) + // Minimum execution time: 30_302_000 picoseconds. + Weight::from_parts(30_830_000, 3574) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(4)) } // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) // Proof Skipped: PolkadotXcm VersionNotifyTargets (max_values: None, max_size: None, mode: Measured) @@ -201,12 +208,14 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_663_000 picoseconds. - Weight::from_parts(4_815_000, 0) + // Minimum execution time: 4_720_000 picoseconds. + Weight::from_parts(4_891_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -221,48 +230,50 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 382_848_000 picoseconds. - Weight::from_parts(398_418_000, 3574) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) + // Minimum execution time: 383_395_000 picoseconds. + Weight::from_parts(392_319_000, 3574) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 115_229_000 picoseconds. - Weight::from_parts(120_694_000, 0) + // Minimum execution time: 120_259_000 picoseconds. + Weight::from_parts(120_965_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_136_000 picoseconds. - Weight::from_parts(12_364_000, 0) + // Minimum execution time: 11_649_000 picoseconds. + Weight::from_parts(11_897_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_669_000 picoseconds. - Weight::from_parts(2_766_000, 0) + // Minimum execution time: 2_629_000 picoseconds. + Weight::from_parts(2_752_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_578_000 picoseconds. - Weight::from_parts(2_664_000, 0) + // Minimum execution time: 2_487_000 picoseconds. + Weight::from_parts(2_570_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_775_000 picoseconds. - Weight::from_parts(2_854_000, 0) + // Minimum execution time: 2_662_000 picoseconds. + Weight::from_parts(2_760_000, 0) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -277,20 +288,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 28_690_000 picoseconds. - Weight::from_parts(29_048_000, 3574) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) + // Minimum execution time: 31_294_000 picoseconds. + Weight::from_parts(31_914_000, 3574) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) } pub fn expect_pallet() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_236_000 picoseconds. - Weight::from_parts(5_347_000, 0) + // Minimum execution time: 5_224_000 picoseconds. + Weight::from_parts(5_385_000, 0) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) + // Proof Skipped: unknown `0x3a696e747261626c6f636b5f656e74726f7079` (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) @@ -305,44 +318,54 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 25_235_000 picoseconds. - Weight::from_parts(26_132_000, 3574) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(2)) + // Minimum execution time: 27_691_000 picoseconds. + Weight::from_parts(28_186_000, 3574) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) } pub fn clear_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_672_000 picoseconds. - Weight::from_parts(2_750_000, 0) + // Minimum execution time: 2_508_000 picoseconds. + Weight::from_parts(2_651_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_590_000 picoseconds. - Weight::from_parts(2_661_000, 0) + // Minimum execution time: 2_557_000 picoseconds. + Weight::from_parts(2_618_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_623_000 picoseconds. - Weight::from_parts(2_712_000, 0) + // Minimum execution time: 2_481_000 picoseconds. + Weight::from_parts(2_552_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: 4_831_000 picoseconds. + Weight::from_parts(4_997_000, 1489) + .saturating_add(T::DbWeight::get().reads(1)) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_664_000 picoseconds. - Weight::from_parts(2_726_000, 0) + // Minimum execution time: 2_545_000 picoseconds. + Weight::from_parts(2_662_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_760_000 picoseconds. - Weight::from_parts(2_852_000, 0) + // Minimum execution time: 2_724_000 picoseconds. + Weight::from_parts(2_863_000, 0) } } diff --git a/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 401a40b434d..bb6515c1ff6 100644 --- a/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -14,11 +14,10 @@ // limitations under the License. use super::{ - AccountId, AllPalletsWithSystem, Assets, Authorship, Balance, Balances, ParachainInfo, - ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, + AccountId, AllPalletsWithSystem, Assets, Authorship, Balance, Balances, ForeignAssets, + ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, TrustBackedAssetsInstance, WeightToFee, XcmpQueue, }; -use crate::ForeignAssets; use assets_common::matching::{ FromSiblingParachain, IsForeignConcreteAsset, StartsWith, StartsWithExplicitGlobalConsensus, }; @@ -35,11 +34,12 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, - DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, - LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, - UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FungiblesAdapter, + GlobalConsensusParachainConvertsFor, IsConcrete, LocalMint, NativeAsset, NoChecking, + ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -65,6 +65,9 @@ pub type LocationToAccountId = ( SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, + // Different global consensus parachain sovereign account. + // (Used for over-bridge transfers and reserve processing) + GlobalConsensusParachainConvertsFor, ); /// Means for transacting the native currency on this chain. @@ -188,6 +191,14 @@ impl Contains for SafeCallFilter { } } + // Allow to change dedicated storage items (called by governance-like) + match call { + RuntimeCall::System(frame_system::Call::set_storage { items }) + if items.iter().any(|(k, _)| k.eq(&bridging::AssetHubKusamaLocalMaxFee::key())) => + return true, + _ => (), + }; + matches!( call, RuntimeCall::PolkadotXcm(pallet_xcm::Call::force_xcm_version { .. }) | @@ -377,10 +388,9 @@ impl xcm_executor::Config for XcmConfig { type XcmSender = XcmRouter; type AssetTransactor = AssetTransactors; type OriginConverter = XcmOriginToTransactDispatchOrigin; - // Asset Hub Westend does not recognize a reserve location for any asset. This does not prevent // Asset Hub acting _as_ a reserve location for WND and assets created under `pallet-assets`. // For WND, users must use teleport where allowed (e.g. with the Relay Chain). - type IsReserve = (); + type IsReserve = bridging::IsTrustedBridgedReserveLocationForConcreteAsset; // We allow: // - teleportation of WND // - teleportation of sibling parachain's assets (as ForeignCreators) @@ -419,7 +429,7 @@ impl xcm_executor::Config for XcmConfig { type AssetExchanger = (); type FeeManager = (); type MessageExporter = (); - type UniversalAliases = Nothing; + type UniversalAliases = bridging::BridgedUniversalAliases; type CallDispatcher = WithOriginFilter; type SafeCallFilter = SafeCallFilter; type Aliasers = Nothing; @@ -493,3 +503,187 @@ impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { MultiLocation { parents: 1, interior: X1(Parachain(id)) } } } + +/// All configuration related to bridging +pub mod bridging { + use super::*; + use pallet_bridge_transfer_primitives::{ + AssetFilter, BridgeConfig, BridgesConfig, BridgesConfigAdapter, BridgesConfigBuilder, + MaybePaidLocation, MultiLocationFilter, ReserveLocation, + }; + use sp_std::collections::btree_set::BTreeSet; + use xcm_builder::UnpaidRemoteExporter; + + parameter_types! { + // used for local testing + pub BridgeHubParaId: u32 = 1002; + pub BridgeHub: MultiLocation = MultiLocation::new(1, X1(Parachain(BridgeHubParaId::get()))); + pub const KusamaLocalNetwork: NetworkId = NetworkId::Kusama; + pub AssetHubKusamaLocal: MultiLocation = MultiLocation::new(2, X2(GlobalConsensus(KusamaLocalNetwork::get()), Parachain(1000))); + // Initial value, this will be adjusted by governance motion on deployment with some more accurate value + pub storage AssetHubKusamaLocalMaxFee: Option = Some((MultiLocation::parent(), 1_000_000).into()); + pub KsmLocation: MultiLocation = MultiLocation::new(2, X1(GlobalConsensus(KusamaLocalNetwork::get()))); + + // Setup bridges configuration + // Allows to transfer only WND to AssetHubKusama + // (hard-coded version - on-chain configuration will come later as separate feature) + pub Bridges: BridgesConfig = BridgesConfigBuilder::default() + // add exporter for Kusama + .add_or_panic( + KusamaLocalNetwork::get(), + BridgeConfig::new( + MaybePaidLocation { + location: BridgeHub::get(), + // Noe fees needed because we use `UnpaidRemoteExporter` and BridgeHubKusama allows unpaid execution for local system parachains + maybe_fee: None, + } + ).add_target_location( + // add target location as AssetHubKusamaLocal + MaybePaidLocation { + location: AssetHubKusamaLocal::get(), + maybe_fee: AssetHubKusamaLocalMaxFee::get(), + }, + Some(AssetFilter::ByMultiLocation( + MultiLocationFilter::default() + // allow transfer WND + .add_equals(WestendLocation::get()) + )) + ) + ) + .build(); + + // Setup trusted bridged reserve locations + pub BridgedReserves: sp_std::vec::Vec = sp_std::vec![ + // trust assets from AssetHubKusamaLocal + ( + AssetHubKusamaLocal::get(), + AssetFilter::ByMultiLocation( + MultiLocationFilter::default() + // allow receive KSM + .add_equals(KsmLocation::get()) + ) + ) + ]; + + /// Universal aliases + pub BridgedUniversalAliases: BTreeSet<(MultiLocation, Junction)> = BTreeSet::from_iter( + sp_std::vec![ + (BridgeHub::get(), GlobalConsensus(KusamaLocalNetwork::get())) + ] + ); + } + + impl Contains<(MultiLocation, Junction)> for BridgedUniversalAliases { + fn contains(alias: &(MultiLocation, Junction)) -> bool { + BridgedUniversalAliases::get().contains(alias) + } + } + + /// Bridge router, which wraps and sends xcm to BridgeHub to be delivered to the different GlobalConsensus + pub type BridgeXcmSender = + UnpaidRemoteExporter, XcmRouter, UniversalLocation>; + + /// Reserve locations filter for `xcm_executor::Config::IsReserve`. + pub type IsTrustedBridgedReserveLocationForConcreteAsset = + pallet_bridge_transfer::features::IsTrustedBridgedReserveLocationForConcreteAsset< + UniversalLocation, + BridgedReserves, + >; +} + +#[cfg(feature = "runtime-benchmarks")] +use pallet_bridge_transfer_primitives::{MaybePaidLocation, ReachableDestination}; + +/// Benchmarks helper for over-bridge transfer pallet. +#[cfg(feature = "runtime-benchmarks")] +pub struct BridgeTransferBenchmarksHelper; +#[cfg(feature = "runtime-benchmarks")] +impl pallet_bridge_transfer::BenchmarkHelper for BridgeTransferBenchmarksHelper { + fn desired_bridged_location() -> Option<(NetworkId, ReachableDestination)> { + let bridged_network = bridging::KusamaLocalNetwork::get(); + let target_location = bridging::AssetHubKusamaLocal::get(); + let target_location_fee = bridging::AssetHubKusamaLocalMaxFee::get(); + let target_location_account = target_location + .clone() + .appended_with(AccountId32 { + network: Some(bridged_network), + id: AccountId::from([42u8; 32]).into(), + }) + .expect("Correct target_location_account"); + + Some(( + bridging::KusamaLocalNetwork::get(), + ReachableDestination { + bridge: MaybePaidLocation { + location: bridging::BridgeHub::get(), + // Right now `UnpaidRemoteExporter` is used to send XCM messages and it requires + // fee to be `None`. If we're going to change that (are we?), then we should replace + // this `None` with `Some(Self::make_asset(crate::ExistentialDeposit::get()))` + maybe_fee: None, + }, + target: MaybePaidLocation { + location: target_location, + maybe_fee: target_location_fee, + }, + target_destination: target_location_account, + }, + )) + } + + fn prepare_asset_transfer_for( + desired_bridged_location: (NetworkId, ReachableDestination), + assumed_reserve_account: MultiLocation, + ) -> Option<(RuntimeOrigin, xcm::VersionedMultiAssets, xcm::VersionedMultiLocation)> { + use frame_support::traits::Currency; + let (_, desired_bridged_location) = desired_bridged_location; + + // our `BridgeXcmSender` assumes that the HRMP channel is opened between this + // parachain and the sibling bridge-hub parachain. + // we expect local bridge-hub + let bridge_hub_para_id = match desired_bridged_location.bridge.location { + MultiLocation { parents: 1, interior: X1(Parachain(bridge_hub_para_id)) } => + bridge_hub_para_id, + _ => panic!("Cannot resolve bridge_hub_para_id"), + }; + cumulus_pallet_parachain_system::Pallet::::open_outbound_hrmp_channel_for_benchmarks( + bridge_hub_para_id.into(), + ); + + // sender account + let sender_account = AccountId::from([42u8; 32]); + // reserve account + use xcm_executor::traits::ConvertLocation; + let assumed_reserve_account = + LocationToAccountId::convert_location(&assumed_reserve_account) + .expect("Correct AccountId"); + + // deposit enough (ED) funds to the sender and reserve account + let existential_deposit = crate::ExistentialDeposit::get(); + let _ = Balances::deposit_creating(&sender_account, existential_deposit * 10); + let _ = Balances::deposit_creating(&assumed_reserve_account, existential_deposit * 10); + + // finally - prepare assets + // lets consider our worst case scenario - reserve based transfer with relay chain tokens + let asset: MultiAsset = (Concrete(WestendLocation::get()), existential_deposit * 2).into(); + + let assets = xcm::VersionedMultiAssets::from(MultiAssets::from(asset)); + let destination = + xcm::VersionedMultiLocation::from(desired_bridged_location.target_destination); + + Some((RuntimeOrigin::signed(sender_account), assets, destination)) + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl BridgeTransferBenchmarksHelper { + pub fn prepare_universal_alias() -> Option<(MultiLocation, Junction)> { + let alias = bridging::BridgedUniversalAliases::get().into_iter().find_map( + |(location, junction)| match bridging::BridgeHub::get().eq(&location) { + true => Some((location, junction)), + false => None, + }, + ); + assert!(alias.is_some(), "we expect here BridgeHub to KusamaLocal mapping at least"); + Some(alias.unwrap()) + } +} diff --git a/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index 4cfed8fed08..6adf0514e4d 100644 --- a/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -19,14 +19,16 @@ pub use asset_hub_westend_runtime::{ constants::fee::WeightToFee, - xcm_config::{CheckingAccount, TrustBackedAssetsPalletLocation, XcmConfig}, + xcm_config::{ + CheckingAccount, LocationToAccountId, TrustBackedAssetsPalletLocation, XcmConfig, + }, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, - ParachainSystem, Runtime, SessionKeys, System, TrustBackedAssetsInstance, + ParachainSystem, Runtime, SessionKeys, System, TrustBackedAssetsInstance, XcmpQueue, }; use asset_hub_westend_runtime::{ xcm_config::{ - AssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf, - WestendLocation, + bridging, AssetFeeAsExistentialDepositMultiplierFeeCharger, + ForeignCreatorsSovereignAccountOf, WestendLocation, }, MetadataDepositBase, MetadataDepositPerByte, RuntimeCall, RuntimeEvent, }; @@ -48,6 +50,7 @@ use xcm_executor::{ }; const ALICE: [u8; 32] = [1u8; 32]; +const BOB: [u8; 32] = [0u8; 32]; const SOME_ASSET_ADMIN: [u8; 32] = [5u8; 32]; type AssetIdForTrustBackedAssetsConvert = @@ -627,6 +630,119 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p }) ); +fn bridging_to_asset_hub_kusama() -> asset_test_utils::test_cases_over_bridge::TestBridgingConfig { + asset_test_utils::test_cases_over_bridge::TestBridgingConfig { + bridged_network: bridging::KusamaLocalNetwork::get(), + local_bridge_hub_para_id: bridging::BridgeHubParaId::get(), + local_bridge_hub_location: pallet_bridge_transfer_primitives::MaybePaidLocation { + location: bridging::BridgeHub::get(), + maybe_fee: None, + }, + bridged_target_location: pallet_bridge_transfer_primitives::MaybePaidLocation { + location: bridging::AssetHubKusamaLocal::get(), + maybe_fee: bridging::AssetHubKusamaLocalMaxFee::get(), + }, + } +} + +#[test] +fn transfer_asset_via_bridge_initiate_reserve_based_for_native_asset_works() { + asset_test_utils::test_cases_over_bridge::transfer_asset_via_bridge_initiate_reserve_based_for_native_asset_works::< + Runtime, + XcmConfig, + ParachainSystem, + XcmpQueue, + LocationToAccountId, + >( + collator_session_keys(), + ExistentialDeposit::get(), + AccountId::from(ALICE), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::BridgeTransfer(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + bridging_to_asset_hub_kusama + ) +} + +#[test] +fn transfer_asset_via_bridge_initiate_withdraw_reserve_for_native_asset_works() { + asset_test_utils::test_cases_over_bridge::transfer_asset_via_bridge_initiate_withdraw_reserve_for_native_asset_works::< + Runtime, + XcmConfig, + ParachainSystem, + XcmpQueue, + LocationToAccountId, + ForeignAssetsInstance, + >( + collator_session_keys(), + ExistentialDeposit::get(), + AccountId::from(ALICE), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::BridgeTransfer(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + bridging_to_asset_hub_kusama + ) +} + +#[test] +fn receive_reserve_asset_deposited_from_different_consensus_over_bridge_works() { + asset_test_utils::test_cases_over_bridge::receive_reserve_asset_deposited_from_different_consensus_over_bridge_works::< + Runtime, + XcmConfig, + LocationToAccountId, + ForeignAssetsInstance, + >( + collator_session_keys(), + ExistentialDeposit::get(), + AccountId::from(BOB), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + bridging_to_asset_hub_kusama, + ) +} + +#[test] +fn withdraw_reserve_asset_deposited_from_different_consensus_over_bridge_works() { + asset_test_utils::test_cases_over_bridge::withdraw_reserve_asset_deposited_from_different_consensus_over_bridge_works::< + Runtime, + XcmConfig, + LocationToAccountId, + >( + collator_session_keys(), + ExistentialDeposit::get(), + AccountId::from(BOB), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + bridging_to_asset_hub_kusama, + ) +} + #[test] fn plain_receive_teleported_asset_works() { ExtBuilder::::default() @@ -652,3 +768,28 @@ fn plain_receive_teleported_asset_works() { assert_eq!(outcome.ensure_complete(), Ok(())); }) } + +#[test] +fn change_asset_hub_kusama_local_max_fee_by_governance_works() { + asset_test_utils::test_cases::change_storage_constant_by_governance_works::< + Runtime, + bridging::AssetHubKusamaLocalMaxFee, + Option, + >( + collator_session_keys(), + 1000, + Box::new(|call| RuntimeCall::System(call).encode()), + || { + ( + bridging::AssetHubKusamaLocalMaxFee::key().to_vec(), + bridging::AssetHubKusamaLocalMaxFee::get(), + ) + }, + |old_value| match old_value { + Some(MultiAsset { id, fun: Fungible(old_amount) }) => + Some(MultiAsset { id: *id, fun: Fungible(old_amount * 2) }), + Some(_) => None, + None => Some(MultiAsset::from((Here, 123456))), + }, + ) +} diff --git a/parachains/runtimes/assets/test-utils/Cargo.toml b/parachains/runtimes/assets/test-utils/Cargo.toml index f683387e6eb..c29db54b46b 100644 --- a/parachains/runtimes/assets/test-utils/Cargo.toml +++ b/parachains/runtimes/assets/test-utils/Cargo.toml @@ -31,10 +31,13 @@ cumulus-primitives-core = { path = "../../../../primitives/core", default-featur cumulus-primitives-parachain-inherent = { path = "../../../../primitives/parachain-inherent", default-features = false } cumulus-test-relay-sproof-builder = { path = "../../../../test/relay-sproof-builder", default-features = false } parachain-info = { path = "../../../../parachains/pallets/parachain-info", default-features = false } +pallet-bridge-transfer = { path = "../../../pallets/bridge-transfer", default-features = false } +pallet-bridge-transfer-primitives = { path = "../../../pallets/bridge-transfer/primitives", default-features = false } parachains-runtimes-test-utils = { path = "../../test-utils", default-features = false } # Polkadot xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } @@ -59,18 +62,21 @@ std = [ "cumulus-pallet-parachain-system/std", "pallet-collator-selection/std", "pallet-session/std", + "pallet-xcm/std", "assets-common/std", "parachains-common/std", - "parachain-info/std", - "parachains-runtimes-test-utils/std", - "polkadot-parachain/std", + "parachain-info/std", + "parachains-runtimes-test-utils/std", + "polkadot-parachain/std", "sp-consensus-aura/std", "sp-io/std", "sp-runtime/std", "sp-std/std", "xcm/std", + "xcm-builder/std", "xcm-executor/std", - "pallet-xcm/std", + "pallet-bridge-transfer/std", + "pallet-bridge-transfer-primitives/std", "cumulus-pallet-xcmp-queue/std", "cumulus-pallet-dmp-queue/std", ] diff --git a/parachains/runtimes/assets/test-utils/src/lib.rs b/parachains/runtimes/assets/test-utils/src/lib.rs index 9a24867592e..bd5e6e7deb4 100644 --- a/parachains/runtimes/assets/test-utils/src/lib.rs +++ b/parachains/runtimes/assets/test-utils/src/lib.rs @@ -17,4 +17,5 @@ //! Module contains predefined test-case scenarios for `Runtime` with various assets. pub mod test_cases; +pub mod test_cases_over_bridge; pub use parachains_runtimes_test_utils::*; diff --git a/parachains/runtimes/assets/test-utils/src/test_cases.rs b/parachains/runtimes/assets/test-utils/src/test_cases.rs index 162239a8d2a..d6ba51bad26 100644 --- a/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -33,6 +33,9 @@ use sp_runtime::{ use xcm::latest::prelude::*; use xcm_executor::{traits::ConvertLocation, XcmExecutor}; +// Re-export +pub use parachains_runtimes_test_utils::change_storage_constant_by_governance_works; + /// Test-case makes sure that `Runtime` can receive native asset from relay chain /// and can teleport it back and to the other parachains pub fn teleports_for_native_asset_works< @@ -704,7 +707,7 @@ macro_rules! include_asset_transactor_transfer_with_local_consensus_currency_wor } ); -///Test-case makes sure that `Runtime`'s `xcm::AssetTransactor` can handle native relay chain currency +/// Test-case makes sure that `Runtime`'s `xcm::AssetTransactor` can handle native relay chain currency pub fn asset_transactor_transfer_with_pallet_assets_instance_works< Runtime, XcmConfig, @@ -964,6 +967,7 @@ macro_rules! include_asset_transactor_transfer_with_pallet_assets_instance_works } ); +/// Test-case makes sure that `Runtime`'s can create and manage `ForeignAssets` pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works< Runtime, XcmConfig, diff --git a/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs b/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs new file mode 100644 index 00000000000..9e552fcf9cb --- /dev/null +++ b/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs @@ -0,0 +1,1116 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Module contains predefined test-case scenarios for `Runtime` with various assets over bridge transfer. + +use codec::Encode; +use cumulus_primitives_core::XcmpMessageSource; +use frame_support::{ + assert_ok, + traits::{Currency, Get, OriginTrait, ProcessMessageError}, +}; +use pallet_bridge_transfer_primitives::MaybePaidLocation; +use parachains_common::Balance; +use parachains_runtimes_test_utils::{ + mock_open_hrmp_channel, AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder, RuntimeHelper, + ValidatorIdOf, XcmReceivedFrom, +}; +use sp_runtime::traits::StaticLookup; +use xcm::{latest::prelude::*, VersionedMultiAssets, VersionedMultiLocation}; +use xcm_builder::{CreateMatcher, MatchXcm}; +use xcm_executor::{traits::ConvertLocation, XcmExecutor}; + +pub struct TestBridgingConfig { + pub bridged_network: NetworkId, + pub local_bridge_hub_para_id: u32, + pub local_bridge_hub_location: MaybePaidLocation, + pub bridged_target_location: MaybePaidLocation, +} + +/// Test-case makes sure that `Runtime` can initiate transfer of assets via bridge - `TransferKind::ReserveBased` +pub fn transfer_asset_via_bridge_initiate_reserve_based_for_native_asset_works< + Runtime, + XcmConfig, + HrmpChannelOpener, + HrmpChannelSource, + LocationToAccountId, +>( + collator_session_keys: CollatorSessionKeys, + existential_deposit: BalanceOf, + alice_account: AccountIdOf, + unwrap_pallet_bridge_transfer_event: Box< + dyn Fn(Vec) -> Option>, + >, + unwrap_xcmp_queue_event: Box< + dyn Fn(Vec) -> Option>, + >, + ensure_configuration: fn() -> TestBridgingConfig, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + pallet_bridge_transfer::Config + + cumulus_pallet_xcmp_queue::Config, + AccountIdOf: Into<[u8; 32]>, + ValidatorIdOf: From>, + BalanceOf: From, + ::Balance: From + Into, + XcmConfig: xcm_executor::Config, + LocationToAccountId: ConvertLocation>, + ::AccountId: + Into<<::RuntimeOrigin as OriginTrait>::AccountId>, + <::Lookup as StaticLookup>::Source: + From<::AccountId>, + HrmpChannelOpener: frame_support::inherent::ProvideInherent< + Call = cumulus_pallet_parachain_system::Call, + >, + HrmpChannelSource: XcmpMessageSource, +{ + let runtime_para_id = 1000; + ExtBuilder::::default() + .with_collators(collator_session_keys.collators()) + .with_session_keys(collator_session_keys.session_keys()) + .with_tracing() + .with_safe_xcm_version(3) + .with_para_id(runtime_para_id.into()) + .build() + .execute_with(|| { + // prepare bridge config + let TestBridgingConfig { + bridged_network, + local_bridge_hub_para_id, + bridged_target_location: + MaybePaidLocation { + location: target_location_from_different_consensus, + maybe_fee: target_location_fee, + }, + .. + } = ensure_configuration(); + + // we expect paid target execution + let target_location_fee = target_location_fee.unwrap(); + + let reserve_account = + LocationToAccountId::convert_location(&target_location_from_different_consensus) + .expect("Sovereign account for reserves"); + let balance_to_transfer = 1000_u128; + let native_asset = MultiLocation::parent(); + + // open HRMP to bridge hub + mock_open_hrmp_channel::( + runtime_para_id.into(), + local_bridge_hub_para_id.into(), + ); + + // drip ED to account + let alice_account_init_balance = existential_deposit + balance_to_transfer.into(); + let _ = >::deposit_creating( + &alice_account, + alice_account_init_balance, + ); + // SA of target location needs to have at least ED, anyway making reserve fails + let _ = >::deposit_creating( + &reserve_account, + existential_deposit, + ); + + // we just check here, that user remains enough balances after withdraw + // and also we check if `balance_to_transfer` is more than `existential_deposit`, + assert!( + (>::free_balance(&alice_account) - + balance_to_transfer.into()) >= + existential_deposit + ); + // SA has just ED + assert_eq!( + >::free_balance(&reserve_account), + existential_deposit + ); + + // local native asset (pallet_balances) + let assets = MultiAssets::from(MultiAsset { + fun: Fungible(balance_to_transfer.into()), + id: Concrete(native_asset), + }); + + // destination is (some) account from different consensus + let target_destination_account = target_location_from_different_consensus + .appended_with(AccountId32 { + network: Some(bridged_network), + id: sp_runtime::AccountId32::new([3; 32]).into(), + }) + .unwrap(); + + // trigger asset transfer + assert_ok!(>::transfer_asset_via_bridge( + RuntimeHelper::::origin_of(alice_account.clone()), + Box::new(VersionedMultiAssets::from(assets.clone())), + Box::new(VersionedMultiLocation::from(target_destination_account)), + )); + + // check alice account decreased + assert_eq!( + >::free_balance(&alice_account), + alice_account_init_balance - balance_to_transfer.into() + ); + // check reserve account increased + assert_eq!( + >::free_balance(&reserve_account), + existential_deposit + balance_to_transfer.into() + ); + + // check events + let mut bridge_transfer_events = >::events() + .into_iter() + .filter_map(|e| unwrap_pallet_bridge_transfer_event(e.event.encode())); + assert!(bridge_transfer_events.any(|r| matches!( + r, + pallet_bridge_transfer::Event::ReserveAssetsDeposited { .. } + ))); + let transfer_initiated_event = bridge_transfer_events.find_map(|e| match e { + pallet_bridge_transfer::Event::TransferInitiated { + message_id, + forwarded_message_id, + sender_cost, + } => Some((message_id, forwarded_message_id, sender_cost)), + _ => None, + }); + assert!(transfer_initiated_event.is_some()); + let (message_id, forwarded_message_id, sender_cost) = transfer_initiated_event.unwrap(); + // we expect UnpaidRemoteExporter + assert!(sender_cost.is_none()); + + // check that xcm was sent + let xcm_sent_message_hash = >::events() + .into_iter() + .filter_map(|e| unwrap_xcmp_queue_event(e.event.encode())) + .find_map(|e| match e { + cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } => + Some(message_hash), + _ => None, + }); + + // read xcm + let xcm_sent = + RuntimeHelper::::take_xcm(local_bridge_hub_para_id.into()) + .unwrap(); + println!("xcm_sent: {:?}", xcm_sent); + assert_eq!( + xcm_sent_message_hash, + Some(xcm_sent.using_encoded(sp_io::hashing::blake2_256)) + ); + let mut xcm_sent: Xcm<()> = xcm_sent.try_into().expect("versioned xcm"); + + // check sent XCM ExportMessage to bridge-hub + assert!(xcm_sent + .0 + .matcher() + .match_next_inst(|instr| match instr { + // first instruction is UNpai (because we have explicit unpaid execution on bridge-hub now) + UnpaidExecution { weight_limit, check_origin } + if weight_limit == &Unlimited && check_origin.is_none() => + Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains UnpaidExecution") + .match_next_inst(|instr| match instr { + // second instruction is ExportMessage + ExportMessage { network, destination, xcm: inner_xcm } => { + assert_eq!(network, &bridged_network); + let (_, target_location_junctions_without_global_consensus) = + target_location_from_different_consensus + .interior + .split_global() + .expect("split works"); + assert_eq!( + destination, + &target_location_junctions_without_global_consensus + ); + + let mut reanchored_assets = assets.clone(); + reanchored_assets + .reanchor( + &target_location_from_different_consensus, + XcmConfig::UniversalLocation::get(), + ) + .expect("reanchored assets"); + let mut reanchored_destination_account = target_destination_account; + reanchored_destination_account + .reanchor( + &target_location_from_different_consensus, + XcmConfig::UniversalLocation::get(), + ) + .expect("reanchored destination account"); + let universal_location_as_sovereign_account_on_target = + ::UniversalLocation::get() + .invert_target(&target_location_from_different_consensus) + .expect("invert_target Universal Location"); + + // match inner xcm + assert!(inner_xcm + .0 + .matcher() + .match_next_inst(|next_instr| match next_instr { + WithdrawAsset(fees) + if fees == &MultiAssets::from(target_location_fee.clone()) => + Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains WithdrawAsset") + .match_next_inst(|next_instr| match next_instr { + BuyExecution { ref fees, ref weight_limit } + if fees == &target_location_fee && + weight_limit == &Unlimited => + Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains BuyExecution") + .match_next_inst(|inner_xcm_instr| match inner_xcm_instr { + ReserveAssetDeposited(ref deposited) + if deposited.eq(&reanchored_assets) => + Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains ReserveAssetDeposited") + .match_next_inst(|inner_xcm_instr| match inner_xcm_instr { + DepositAsset { assets: filter, ref beneficiary } + if filter == + &MultiAssetFilter::from(reanchored_assets.clone()) && + beneficiary.eq(&reanchored_destination_account) => + Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains DepositAsset") + .match_next_inst(|inner_xcm_instr| match inner_xcm_instr { + RefundSurplus => Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains RefundSurplus") + .match_next_inst(|inner_xcm_instr| { + match inner_xcm_instr { + DepositAsset { assets: filter, ref beneficiary } + if filter == + &MultiAssetFilter::from( + target_location_fee.clone(), + ) && beneficiary.eq( + &universal_location_as_sovereign_account_on_target, + ) => + Ok(()), + _ => Err(ProcessMessageError::BadFormat), + } + }) + .expect("contains DepositAsset") + .match_next_inst(|instr| match instr { + SetTopic(ref topic) if topic.eq(&message_id) => Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains SetTopic") + .assert_remaining_insts(0) + .is_ok()); + Ok(()) + }, + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains ExportMessage") + .match_next_inst(|instr| match instr { + SetTopic(ref topic) if topic.eq(&forwarded_message_id) => Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains SetTopic") + .assert_remaining_insts(0) + .is_ok()); + }) +} + +/// Test-case makes sure that `Runtime` can initiate transfer of assets via bridge - `TransferKind::WithdrawReserve` +pub fn transfer_asset_via_bridge_initiate_withdraw_reserve_for_native_asset_works< + Runtime, + XcmConfig, + HrmpChannelOpener, + HrmpChannelSource, + LocationToAccountId, + ForeignAssetsPalletInstance, +>( + collator_session_keys: CollatorSessionKeys, + existential_deposit: BalanceOf, + alice_account: AccountIdOf, + unwrap_pallet_bridge_transfer_event: Box< + dyn Fn(Vec) -> Option>, + >, + unwrap_xcmp_queue_event: Box< + dyn Fn(Vec) -> Option>, + >, + ensure_configuration: fn() -> TestBridgingConfig, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + pallet_assets::Config + + pallet_bridge_transfer::Config + + cumulus_pallet_xcmp_queue::Config, + AccountIdOf: Into<[u8; 32]>, + ValidatorIdOf: From>, + BalanceOf: From, + ::Balance: From + Into, + XcmConfig: xcm_executor::Config, + LocationToAccountId: ConvertLocation>, + ::AccountId: + Into<<::RuntimeOrigin as OriginTrait>::AccountId>, + <::Lookup as StaticLookup>::Source: + From<::AccountId>, + HrmpChannelOpener: frame_support::inherent::ProvideInherent< + Call = cumulus_pallet_parachain_system::Call, + >, + HrmpChannelSource: XcmpMessageSource, + >::AssetId: + From + Into, + >::AssetIdParameter: + From + Into, + >::Balance: + From + Into, + ForeignAssetsPalletInstance: 'static, +{ + let runtime_para_id = 1000; + ExtBuilder::::default() + .with_collators(collator_session_keys.collators()) + .with_session_keys(collator_session_keys.session_keys()) + .with_tracing() + .with_safe_xcm_version(3) + .with_para_id(runtime_para_id.into()) + .build() + .execute_with(|| { + // prepare bridge config + let TestBridgingConfig { + bridged_network, + local_bridge_hub_para_id, + bridged_target_location: + MaybePaidLocation { + location: target_location_from_different_consensus, + maybe_fee: target_location_fee, + }, + .. + } = ensure_configuration(); + + // we expect paid target execution + let target_location_fee = target_location_fee.unwrap(); + + let foreign_asset_id_multilocation = + MultiLocation::new(2, X1(GlobalConsensus(bridged_network))); + + let reserve_account = + LocationToAccountId::convert_location(&target_location_from_different_consensus) + .expect("Sovereign account for reserves"); + let balance_to_transfer = 1000_u128; + let asset_minimum_asset_balance = 1_000_000_u128; + + // open HRMP to bridge hub + mock_open_hrmp_channel::( + runtime_para_id.into(), + local_bridge_hub_para_id.into(), + ); + + // drip ED to account + let _ = >::deposit_creating( + &alice_account, + existential_deposit, + ); + // SA of target location needs to have at least ED, anyway making reserve fails + let _ = >::deposit_creating( + &reserve_account, + existential_deposit, + ); + + // user already received native tokens from bridged chain, which are stored in `ForeignAssets` + { + //1. create foreign asset + assert_ok!( + >::force_create( + RuntimeHelper::::root_origin(), + foreign_asset_id_multilocation.into(), + reserve_account.clone().into(), + false, + asset_minimum_asset_balance.into() + ) + ); + + // 2. drip asset to alice + assert_ok!(>::mint( + RuntimeHelper::::origin_of(reserve_account.clone()), + foreign_asset_id_multilocation.into(), + alice_account.clone().into(), + (asset_minimum_asset_balance + balance_to_transfer).into() + )); + } + + assert_eq!( + >::free_balance(&alice_account), + existential_deposit + ); + assert_eq!( + >::free_balance(&reserve_account), + existential_deposit + ); + assert_eq!( + >::balance( + foreign_asset_id_multilocation.into(), + alice_account.clone() + ), + (asset_minimum_asset_balance + balance_to_transfer).into() + ); + + // lets withdraw previously reserve asset deposited from `ForeignAssets` + let assets = MultiAssets::from(MultiAsset { + fun: Fungible(balance_to_transfer.into()), + id: Concrete(foreign_asset_id_multilocation), + }); + + // destination is (some) account from different consensus + let target_destination_account = target_location_from_different_consensus + .appended_with(AccountId32 { + network: Some(bridged_network), + id: sp_runtime::AccountId32::new([3; 32]).into(), + }) + .unwrap(); + + // trigger asset transfer + assert_ok!(>::transfer_asset_via_bridge( + RuntimeHelper::::origin_of(alice_account.clone()), + Box::new(VersionedMultiAssets::from(assets.clone())), + Box::new(VersionedMultiLocation::from(target_destination_account)), + )); + + // check alice account (balances not changed) + assert_eq!( + >::free_balance(&alice_account), + existential_deposit + ); + // check reserve account (balances not changed) + assert_eq!( + >::free_balance(&reserve_account), + existential_deposit + ); + // `ForeignAssets` for alice account is decressed + assert_eq!( + >::balance( + foreign_asset_id_multilocation.into(), + alice_account.clone() + ), + asset_minimum_asset_balance.into() + ); + + // check events + let mut bridge_transfer_events = >::events() + .into_iter() + .filter_map(|e| unwrap_pallet_bridge_transfer_event(e.event.encode())); + assert!(bridge_transfer_events + .any(|r| matches!(r, pallet_bridge_transfer::Event::AssetsWithdrawn { .. }))); + let transfer_initiated_event = bridge_transfer_events.find_map(|e| match e { + pallet_bridge_transfer::Event::TransferInitiated { + message_id, + forwarded_message_id, + sender_cost, + } => Some((message_id, forwarded_message_id, sender_cost)), + _ => None, + }); + assert!(transfer_initiated_event.is_some()); + let (message_id, forwarded_message_id, sender_cost) = transfer_initiated_event.unwrap(); + // we expect UnpaidRemoteExporter + assert!(sender_cost.is_none()); + + // check that xcm was sent + let xcm_sent_message_hash = >::events() + .into_iter() + .filter_map(|e| unwrap_xcmp_queue_event(e.event.encode())) + .find_map(|e| match e { + cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } => + Some(message_hash), + _ => None, + }); + + // read xcm + let xcm_sent = + RuntimeHelper::::take_xcm(local_bridge_hub_para_id.into()) + .unwrap(); + println!("xcm_sent: {:?}", xcm_sent); + assert_eq!( + xcm_sent_message_hash, + Some(xcm_sent.using_encoded(sp_io::hashing::blake2_256)) + ); + let mut xcm_sent: Xcm<()> = xcm_sent.try_into().expect("versioned xcm"); + + // check sent XCM ExportMessage to bridge-hub + assert!(xcm_sent + .0 + .matcher() + .match_next_inst(|instr| match instr { + // first instruction is UNpai (because we have explicit unpaid execution on bridge-hub now) + UnpaidExecution { weight_limit, check_origin } + if weight_limit == &Unlimited && check_origin.is_none() => + Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains UnpaidExecution") + .match_next_inst(|instr| match instr { + // second instruction is ExportMessage + ExportMessage { network, destination, xcm: inner_xcm } => { + assert_eq!(network, &bridged_network); + let (_, target_location_junctions_without_global_consensus) = + target_location_from_different_consensus + .interior + .split_global() + .expect("split works"); + assert_eq!( + destination, + &target_location_junctions_without_global_consensus + ); + + let mut reanchored_assets = assets.clone(); + reanchored_assets + .reanchor( + &target_location_from_different_consensus, + XcmConfig::UniversalLocation::get(), + ) + .expect("reanchored assets"); + let mut reanchored_destination_account = target_destination_account; + reanchored_destination_account + .reanchor( + &target_location_from_different_consensus, + XcmConfig::UniversalLocation::get(), + ) + .expect("reanchored destination account"); + let universal_location_as_sovereign_account_on_target = + ::UniversalLocation::get() + .invert_target(&target_location_from_different_consensus) + .expect("invert_target Universal Location"); + + // match inner xcm + assert!(inner_xcm + .0 + .matcher() + .match_next_inst(|next_instr| match next_instr { + WithdrawAsset(fees) + if fees == &MultiAssets::from(target_location_fee.clone()) => + Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains WithdrawAsset") + .match_next_inst(|next_instr| match next_instr { + BuyExecution { ref fees, ref weight_limit } + if fees == &target_location_fee && + weight_limit == &Unlimited => + Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains BuyExecution") + .match_next_inst(|inner_xcm_instr| match inner_xcm_instr { + WithdrawAsset(ref deposited) + if deposited.eq(&reanchored_assets) => + Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains WithdrawAsset") + .match_next_inst(|inner_xcm_instr| match inner_xcm_instr { + DepositAsset { assets: filter, ref beneficiary } + if filter == + &MultiAssetFilter::from(reanchored_assets.clone()) && + beneficiary.eq(&reanchored_destination_account) => + Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains DepositAsset") + .match_next_inst(|inner_xcm_instr| match inner_xcm_instr { + RefundSurplus => Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains RefundSurplus") + .match_next_inst(|inner_xcm_instr| { + match inner_xcm_instr { + DepositAsset { assets: filter, ref beneficiary } + if filter == + &MultiAssetFilter::from( + target_location_fee.clone(), + ) && beneficiary.eq( + &universal_location_as_sovereign_account_on_target, + ) => + Ok(()), + _ => Err(ProcessMessageError::BadFormat), + } + }) + .expect("contains DepositAsset") + .match_next_inst(|instr| match instr { + SetTopic(ref topic) if topic.eq(&message_id) => Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains SetTopic") + .assert_remaining_insts(0) + .is_ok()); + Ok(()) + }, + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains ExportMessage") + .match_next_inst(|instr| match instr { + SetTopic(ref topic) if topic.eq(&forwarded_message_id) => Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains SetTopic") + .assert_remaining_insts(0) + .is_ok()); + }) +} + +/// Test-case makes sure that `Runtime` can process `ReserveAssetDeposited`. +pub fn receive_reserve_asset_deposited_from_different_consensus_over_bridge_works< + Runtime, + XcmConfig, + LocationToAccountId, + ForeignAssetsPalletInstance, +>( + collator_session_keys: CollatorSessionKeys, + existential_deposit: BalanceOf, + target_account: AccountIdOf, + unwrap_pallet_xcm_event: Box) -> Option>>, + ensure_configuration: fn() -> TestBridgingConfig, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + cumulus_pallet_xcmp_queue::Config + + pallet_assets::Config + + pallet_bridge_transfer::Config, + AccountIdOf: Into<[u8; 32]>, + ValidatorIdOf: From>, + BalanceOf: From, + ::AccountId: + Into<<::RuntimeOrigin as OriginTrait>::AccountId>, + <::Lookup as StaticLookup>::Source: + From<::AccountId>, + XcmConfig: xcm_executor::Config, + >::AssetId: + From + Into, + >::AssetIdParameter: + From + Into, + >::Balance: + From + Into, + LocationToAccountId: ConvertLocation>, + ForeignAssetsPalletInstance: 'static, +{ + ExtBuilder::::default() + .with_collators(collator_session_keys.collators()) + .with_session_keys(collator_session_keys.session_keys()) + .with_balances(vec![(target_account.clone(), existential_deposit)]) + .with_tracing() + .build() + .execute_with(|| { + // prepare bridge config + let TestBridgingConfig { + bridged_network: remote_network_id, + local_bridge_hub_location: + MaybePaidLocation { location: local_bridge_hub_location, .. }, + bridged_target_location: + MaybePaidLocation { location: remote_parachain_as_origin, .. }, + .. + } = ensure_configuration(); + + let foreign_asset_id_multilocation = + MultiLocation { parents: 2, interior: X1(GlobalConsensus(remote_network_id)) }; + + let buy_execution_fee_amount = 50000000000; + let reserve_asset_deposisted = 100_000_000; + + // drip SA for remote global parachain origin + let remote_parachain_sovereign_account = + LocationToAccountId::convert_location(&remote_parachain_as_origin) + .expect("Sovereign account works"); + assert_ok!(>::force_set_balance( + RuntimeHelper::::root_origin(), + remote_parachain_sovereign_account.clone().into(), + existential_deposit + buy_execution_fee_amount.into(), + )); + + // create foreign asset + let asset_minimum_asset_balance = 1_000_000_u128; + assert_ok!( + >::force_create( + RuntimeHelper::::root_origin(), + foreign_asset_id_multilocation.into(), + remote_parachain_sovereign_account.clone().into(), + false, + asset_minimum_asset_balance.into() + ) + ); + + // we assume here that BuyExecution fee goes to staking pot + let staking_pot_account_id = >::account_id(); + assert_ok!(>::force_set_balance( + RuntimeHelper::::root_origin(), + staking_pot_account_id.clone().into(), + existential_deposit, + )); + + let local_bridge_hub_multilocation_as_account_id = + LocationToAccountId::convert_location(&local_bridge_hub_location) + .expect("Correct AccountId"); + + // check before + let remote_parachain_sovereign_account_balance_before = + >::free_balance( + &remote_parachain_sovereign_account, + ); + assert_eq!( + remote_parachain_sovereign_account_balance_before, + existential_deposit + buy_execution_fee_amount.into() + ); + assert_eq!( + >::free_balance(&target_account), + existential_deposit + ); + assert_eq!( + >::free_balance( + &local_bridge_hub_multilocation_as_account_id + ), + 0.into() + ); + assert_eq!( + >::free_balance(&staking_pot_account_id), + existential_deposit + ); + assert_eq!( + >::balance( + foreign_asset_id_multilocation.into(), + &target_account + ), + 0.into() + ); + + // xcm + let xcm = Xcm(vec![ + UniversalOrigin(GlobalConsensus(remote_network_id)), + DescendOrigin(X1(Parachain(1000))), + // buying execution as sovereign account `remote_parachain_sovereign_account` in *native asset on receiving runtime* + WithdrawAsset(MultiAssets::from(vec![MultiAsset { + id: Concrete(MultiLocation { parents: 1, interior: Here }), + fun: Fungible(buy_execution_fee_amount), + }])), + BuyExecution { + fees: MultiAsset { + id: Concrete(MultiLocation { parents: 1, interior: Here }), + fun: Fungible(buy_execution_fee_amount), + }, + weight_limit: Unlimited, + }, + // reserve deposited - assets transferred through bridge - *native asset on sending runtime* + ReserveAssetDeposited(MultiAssets::from(vec![MultiAsset { + id: Concrete(MultiLocation { + parents: 2, + interior: X1(GlobalConsensus(remote_network_id)), + }), + fun: Fungible(reserve_asset_deposisted), + }])), + DepositAsset { + assets: Definite(MultiAssets::from(vec![MultiAsset { + id: Concrete(MultiLocation { + parents: 2, + interior: X1(GlobalConsensus(remote_network_id)), + }), + fun: Fungible(reserve_asset_deposisted), + }])), + beneficiary: MultiLocation { + parents: 0, + interior: X1(AccountId32 { + network: None, + id: target_account.clone().into(), + }), + }, + }, + // return unspent weight back to SA of caller + RefundSurplus, + DepositAsset { + assets: Definite(MultiAssets::from(vec![MultiAsset { + id: Concrete(MultiLocation { parents: 1, interior: Here }), + fun: Fungible(buy_execution_fee_amount), + }])), + beneficiary: remote_parachain_as_origin, + }, + ]); + + // origin as BridgeHub + let origin = local_bridge_hub_location; + + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + + // execute xcm as XcmpQueue would do + let outcome = XcmExecutor::::execute_xcm( + origin, + xcm, + hash, + RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), + ); + assert_eq!(outcome.ensure_complete(), Ok(())); + + // check after + let expected_buy_execution_fee = + >::free_balance(&staking_pot_account_id) - + existential_deposit; + assert_eq!( + >::free_balance( + &remote_parachain_sovereign_account + ), + remote_parachain_sovereign_account_balance_before - expected_buy_execution_fee + ); + assert_eq!( + >::free_balance(&target_account), + existential_deposit + ); + assert_eq!( + >::free_balance( + &local_bridge_hub_multilocation_as_account_id + ), + 0.into() + ); + assert_ne!( + >::free_balance(&staking_pot_account_id), + 0.into() + ); + assert_eq!( + >::balance( + foreign_asset_id_multilocation.into(), + &target_account + ), + reserve_asset_deposisted.into() + ); + + // check NO asset trap occurred + assert_eq!( + false, + >::events() + .into_iter() + .filter_map(|e| unwrap_pallet_xcm_event(e.event.encode())) + .any(|e| matches!(e, pallet_xcm::Event::AssetsTrapped { .. })) + ); + }) +} + +/// Test-case makes sure that `Runtime` can process reserve withdraw which was sent over bridge. +pub fn withdraw_reserve_asset_deposited_from_different_consensus_over_bridge_works< + Runtime, + XcmConfig, + LocationToAccountId, +>( + collator_session_keys: CollatorSessionKeys, + existential_deposit: BalanceOf, + target_account: AccountIdOf, + unwrap_pallet_xcm_event: Box) -> Option>>, + ensure_configuration: fn() -> TestBridgingConfig, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + cumulus_pallet_xcmp_queue::Config + + pallet_bridge_transfer::Config, + AccountIdOf: Into<[u8; 32]>, + ValidatorIdOf: From>, + BalanceOf: From, + ::AccountId: + Into<<::RuntimeOrigin as OriginTrait>::AccountId>, + <::Lookup as StaticLookup>::Source: + From<::AccountId>, + XcmConfig: xcm_executor::Config, + LocationToAccountId: ConvertLocation>, +{ + ExtBuilder::::default() + .with_collators(collator_session_keys.collators()) + .with_session_keys(collator_session_keys.session_keys()) + .with_balances(vec![(target_account.clone(), existential_deposit)]) + .with_tracing() + .build() + .execute_with(|| { + // prepare bridge config + let TestBridgingConfig { + bridged_network: remote_network_id, + local_bridge_hub_location: + MaybePaidLocation { location: local_bridge_hub_location, .. }, + bridged_target_location: + MaybePaidLocation { location: remote_parachain_as_origin, .. }, + .. + } = ensure_configuration(); + + let buy_execution_fee_amount = 50000000000; + let reserve_asset_deposisted = 100_000_000; + + // add reserved assets to SA for remote global parachain origin (this is how reserve was done, when reserve_asset_deposisted was transferred out) + let remote_parachain_sovereign_account = + LocationToAccountId::convert_location(&remote_parachain_as_origin) + .expect("Sovereign account works"); + assert_ok!(>::force_set_balance( + RuntimeHelper::::root_origin(), + remote_parachain_sovereign_account.clone().into(), + existential_deposit + + buy_execution_fee_amount.into() + + reserve_asset_deposisted.into(), + )); + + // we assume here that BuyExecution fee goes to staking pot + let staking_pot_account_id = >::account_id(); + assert_ok!(>::force_set_balance( + RuntimeHelper::::root_origin(), + staking_pot_account_id.clone().into(), + existential_deposit, + )); + + let local_bridge_hub_multilocation_as_account_id = + LocationToAccountId::convert_location(&local_bridge_hub_location) + .expect("Correct AccountId"); + + // check before + let remote_parachain_sovereign_account_balance_before = + >::free_balance( + &remote_parachain_sovereign_account, + ); + assert_eq!( + remote_parachain_sovereign_account_balance_before, + existential_deposit + + buy_execution_fee_amount.into() + + reserve_asset_deposisted.into() + ); + assert_eq!( + >::free_balance(&target_account), + existential_deposit + ); + assert_eq!( + >::free_balance( + &local_bridge_hub_multilocation_as_account_id + ), + 0.into() + ); + assert_eq!( + >::free_balance(&staking_pot_account_id), + existential_deposit + ); + + // xcm + let xcm = Xcm(vec![ + UniversalOrigin(GlobalConsensus(remote_network_id)), + DescendOrigin(X1(Parachain(1000))), + // buying execution as sovereign account `remote_parachain_sovereign_account` in *native asset on receiving runtime* + WithdrawAsset(MultiAssets::from(vec![MultiAsset { + id: Concrete(MultiLocation { parents: 1, interior: Here }), + fun: Fungible(buy_execution_fee_amount), + }])), + BuyExecution { + fees: MultiAsset { + id: Concrete(MultiLocation { parents: 1, interior: Here }), + fun: Fungible(buy_execution_fee_amount), + }, + weight_limit: Unlimited, + }, + // we are returning reserve deposited - assets transferred through bridge - *native asset on receiving runtime* + WithdrawAsset(MultiAssets::from(vec![MultiAsset { + id: Concrete(MultiLocation { parents: 1, interior: Here }), + fun: Fungible(reserve_asset_deposisted), + }])), + DepositAsset { + assets: Definite(MultiAssets::from(vec![MultiAsset { + id: Concrete(MultiLocation { parents: 1, interior: Here }), + fun: Fungible(reserve_asset_deposisted), + }])), + beneficiary: MultiLocation { + parents: 0, + interior: X1(AccountId32 { + network: None, + id: target_account.clone().into(), + }), + }, + }, + // return unspent weight back to SA of caller + RefundSurplus, + DepositAsset { + assets: Definite(MultiAssets::from(vec![MultiAsset { + id: Concrete(MultiLocation { parents: 1, interior: Here }), + fun: Fungible(buy_execution_fee_amount), + }])), + beneficiary: remote_parachain_as_origin, + }, + ]); + + // origin as BridgeHub + let origin = local_bridge_hub_location; + + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + + // execute xcm as XcmpQueue would do + let outcome = XcmExecutor::::execute_xcm( + origin, + xcm, + hash, + RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), + ); + assert_eq!(outcome.ensure_complete(), Ok(())); + + // check after + let expected_buy_execution_fee = + >::free_balance(&staking_pot_account_id) - + existential_deposit; + // check if SA reserve was withdrawn + assert_eq!( + >::free_balance( + &remote_parachain_sovereign_account + ), + remote_parachain_sovereign_account_balance_before - + expected_buy_execution_fee - + reserve_asset_deposisted.into() + ); + // here target_account received reserve + assert_eq!( + >::free_balance(&target_account), + existential_deposit + reserve_asset_deposisted.into() + ); + assert_eq!( + >::free_balance( + &local_bridge_hub_multilocation_as_account_id + ), + 0.into() + ); + assert_ne!( + >::free_balance(&staking_pot_account_id), + 0.into() + ); + + // check NO asset trap occurred + assert_eq!( + false, + >::events() + .into_iter() + .filter_map(|e| unwrap_pallet_xcm_event(e.event.encode())) + .any(|e| matches!(e, pallet_xcm::Event::AssetsTrapped { .. })) + ); + }) +} diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index b187f1da2db..27ecae8a9bc 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -1,15 +1,15 @@ - [Bridge-hub Parachains](#bridge-hub-parachains) - * [Requirements for local run/testing](#requirements-for-local-run-testing) - * [How to test locally Rococo <-> Wococo bridge](#how-to-test-locally-rococo-----wococo-bridge) - + [Run chains (Rococo + BridgeHub, Wococo + BridgeHub) with zombienet](#run-chains--rococo---bridgehub--wococo---bridgehub--with-zombienet) - + [Run relayer (BridgeHubRococo, BridgeHubWococo)](#run-relayer--bridgehubrococo--bridgehubwococo-) - - [Run with script (alternative 1)](#run-with-script--alternative-1-) - - [Run with binary (alternative 2)](#run-with-binary--alternative-2-) - + [Send messages](#send-messages) - - [Local zombienet run](#local-zombienet-run) - - [Live Rockmine2 to Wockmint](#live-rockmine2-to-wockmint) - * [How to test local BridgeHubKusama](#how-to-test-local-bridgehubkusama) - * [How to test local BridgeHubPolkadot](#how-to-test-local-bridgehubpolkadot) + * [Requirements for local run/testing](#requirements-for-local-run-testing) + * [How to test locally Rococo <-> Wococo bridge](#how-to-test-locally-rococo-----wococo-bridge) + + [Run chains (Rococo + BridgeHub, Wococo + BridgeHub) with zombienet](#run-chains--rococo---bridgehub--wococo---bridgehub--with-zombienet) + + [Run relayer (BridgeHubRococo, BridgeHubWococo)](#run-relayer--bridgehubrococo--bridgehubwococo-) + - [Run with script (alternative 1)](#run-with-script--alternative-1-) + - [Run with binary (alternative 2)](#run-with-binary--alternative-2-) + + [Send messages - transfer asset over bridge](#send-messages---transfer-asset-over-bridge) + * [How to test locally Kusama <-> Polkadot bridge](#how-to-test-locally-kusama-----polkadot-bridge) + + [1. Run chains (Kusama + BridgeHub + AssetHub, Polkadot + BridgeHub + AssetHub) with zombienet](#1-run-chains--kusama---bridgehub---assethub--polkadot---bridgehub---assethub--with-zombienet) + + [2. Init bridge and run relayer (BridgeHubKusama, BridgeHubPolkadot)](#2-init-bridge-and-run-relayer--bridgehubkusama--bridgehubpolkadot-) + + [Send messages - transfer asset over bridge](#send-messages---transfer-asset-over-bridge-1) # Bridge-hub Parachains @@ -49,41 +49,42 @@ cd polkadot # if you want to test Kusama/Polkadot bridge, we need "sudo pallet + fast-runtime", # so please, find the latest polkadot's repository branch `it/release-vX.Y.Z-fast-sudo` # e.g: -# git checkout -b it/release-v0.9.42-fast-sudo --track origin/it/release-v0.9.42-fast-sudo +# git checkout -b it/release-v0.9.43-fast-sudo --track origin/it/release-v0.9.43-fast-sudo cargo build --release --features fast-runtime cp target/release/polkadot ~/local_bridge_testing/bin/polkadot --- -# 3. Build cumulus polkadot-parachain binary -cd - -# checkout desired branch or use master: -# git checkout -b master --track origin/master - -cargo build --release --locked -p polkadot-parachain-bin -cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain -cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain-mint - ---- -# 4. Build substrate-relay binary +# 3. Build substrate-relay binary git clone https://github.com/paritytech/parity-bridges-common.git cd parity-bridges-common # checkout desired branch or use master: # git checkout -b master --track origin/master +# `polkadot-staging` (recommended) is stabilized and compatible for Cumulus releases +# `master` is latest development git checkout -b polkadot-staging --track origin/polkadot-staging cargo build --release -p substrate-relay cp target/release/substrate-relay ~/local_bridge_testing/bin/substrate-relay --- -# 5. Build polkadot-parachain-mint binary with `asset-hub-kusama`/`asset-hub-westend` for moving assets +# 4. Build cumulus polkadot-parachain binary cd -# TODO:check-parameter - change this when merged to master -git checkout -b bko-transfer-asset-via-bridge --track origin/bko-transfer-asset-via-bridge -cargo build --release --locked -p polkadot-parachain-bin -cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain-mint + +# checkout desired branch or use master: +# git checkout -b master --track origin/master + +# !!! READ HERE (TODO remove once merged) +# The use case "moving assets over bridge" is not merged yet and is implemented in separate branches. +# So, if you want to try it, you need to checkout different branch and continue with these instructions there. +# +# For Rococo/Wococo local/onchain bridge testing: +# git checkout -b bko-transfer-asset-via-bridge-ro-wo --track origin/bko-transfer-asset-via-bridge-ro-wo + +cargo build --release --locked --bin polkadot-parachain +cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain +cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain-asset-hub ``` ## How to test locally Rococo <-> Wococo bridge @@ -91,18 +92,18 @@ cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-paracha ### Run chains (Rococo + BridgeHub, Wococo + BridgeHub) with zombienet ``` -# Rococo + BridgeHubRococo + Rockmine (mirroring Kusama) +# Rococo + BridgeHubRococo + AssetHub for Rococo (mirroring Kusama) POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ -POLKADOT_PARACHAIN_BINARY_PATH_FOR_ROCKMINE=~/local_bridge_testing/bin/polkadot-parachain-mint \ +POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_ROCOCO=~/local_bridge_testing/bin/polkadot-parachain-asset-hub \ ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml ``` ``` -# Wococo + BridgeHubWococo + Wockmint (mirroring Polkadot) +# Wococo + BridgeHubWococo + AssetHub for Wococo (mirroring Polkadot) POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ -POLKADOT_PARACHAIN_BINARY_PATH_FOR_WOCKMINT=~/local_bridge_testing/bin/polkadot-parachain-mint \ +POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_WOCOCO=~/local_bridge_testing/bin/polkadot-parachain-asset-hub \ ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml ``` @@ -191,65 +192,54 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - Pallet: **bridgeRococoParachain** - Keys: **bestParaHeads()** -### Send messages +### Send messages - transfer asset over bridge -#### Local zombienet run +TODO: see `# !!! READ HERE` above -1. allow bridge transfer on kusama/westend asset hubs (governance-like): - ``` - ./scripts/bridges_rococo_wococo.sh allow-transfers-local - ``` +## How to test locally Kusama <-> Polkadot bridge -2. do (asset) transfer from kusama's asset hub to westend's asset hub: - ``` - ./scripts/bridges_rococo_wococo.sh transfer-asset-from-asset-hub-kusama-local - ``` +Check [requirements](#requirements-for-local-runtesting) for "sudo pallet + fast-runtime". -3. do (ping) transfer from kusama's asset hub to westend's asset hub - ``` - ./scripts/bridges_rococo_wococo.sh ping-via-bridge-from-asset-hub-kusama-local - ``` - -- open explorers: (see zombienets) - - Kusama Asset Hub (see events `xcmpQueue.XcmpMessageSent`, `bridgeTransfer.ReserveAssetsDeposited`, `bridgeTransfer.TransferInitiated`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9910#/explorer - - BridgeHubRococo (see `bridgeWococoMessages.MessageAccepted`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8943#/explorer - - BridgeHubWococo (see `bridgeRococoMessages.MessagesReceived`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8945#/explorer - - Westend Asset Hub (see `xcmpQueue.Success` for `transfer-asset` and `xcmpQueue.Fail` for `ping-via-bridge`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9010#/explorer - - BridgeHubRococo (see `bridgeWococoMessages.MessagesDelivered`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8943#/explorer +### 1. Run chains (Kusama + BridgeHub + AssetHub, Polkadot + BridgeHub + AssetHub) with zombienet -#### Live Rockmine2 to Wockmint -- uses account seed on Live Rococo:Rockmine2 - ``` - cd +``` +# Kusama + BridgeHubKusama + AssetHubKusama +POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ +POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ +POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_KUSAMA=~/local_bridge_testing/bin/polkadot-parachain-asset-hub \ + ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet/bridge-hubs/bridge_hub_kusama_local_network.toml +``` - ./scripts/bridges_rococo_wococo.sh transfer-asset-from-asset-hub-rococo - or - ./scripts/bridges_rococo_wococo.sh ping-via-bridge-from-asset-hub-rococo - ``` +``` +# Polkadot + BridgeHubPolkadot + AssetHubPolkadot +POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ +POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ +POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_POLKADOT=~/local_bridge_testing/bin/polkadot-parachain-asset-hub \ + ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet/bridge-hubs/bridge_hub_polkadot_local_network.toml +``` -- open explorers: - - Rockmine2 (see events `xcmpQueue.XcmpMessageSent`, `bridgeTransfer.ReserveAssetsDeposited`, `bridgeTransfer.TransferInitiated`) https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fws-rococo-rockmine2-collator-node-0.parity-testnet.parity.io#/explorer - - BridgeHubRococo (see `bridgeWococoMessages.MessageAccepted`) https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frococo-bridge-hub-rpc.polkadot.io#/explorer - - BridgeHubWococo (see `bridgeRococoMessages.MessagesReceived`) https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fwococo-bridge-hub-rpc.polkadot.io#/explorer - - Wockmint (see `xcmpQueue.Success` for `transfer-asset` and `xcmpQueue.Fail` for `ping-via-bridge`) https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fws-wococo-wockmint-collator-node-0.parity-testnet.parity.io#/explorer - - BridgeHubRococo (see `bridgeWococoMessages.MessagesDelivered`) +### 2. Init bridge and run relayer (BridgeHubKusama, BridgeHubPolkadot) -## How to test local BridgeHubKusama ``` -cd -cargo build --release -p polkadot-parachain-bin - -# script expect to have pre-built polkadot binary on the path: ../polkadot/target/release/polkadot -# if using `kusama-local` / `polkadot-local`, build polkadot with `--features fast-runtime` +cd +./scripts/bridges_kusama_polkadot.sh run-relay +``` -# BridgeHubKusama -zombienet-linux --provider native spawn ./zombienet/examples/bridge_hub_kusama_local_network.toml +### Send messages - transfer asset over bridge -or +Drip SA for AssetHubKusama on AssetHubStatemint. +``` +./scripts/bridges_kusama_polkadot.sh drip +``` -# BridgeHubPolkadot -zombienet-linux --provider native spawn ./zombienet/examples/bridge_hub_polkadot_local_network.toml +Do (asset) transfer from Kusama's Asset Hub to Polkadot's. +``` +./scripts/bridges_kusama_polkadot.sh transfer-asset-from-asset-hub-kusama-local ``` -## How to test local BridgeHubPolkadot -TODO: from master +- open explorers: (see zombienets) + - AssetHubKusama (see events `xcmpQueue.XcmpMessageSent`, `bridgeTransfer.ReserveAssetsDeposited`, `bridgeTransfer.TransferInitiated`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9910#/explorer + - BridgeHubKusama (see `bridgePolkadotMessages.MessageAccepted`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8943#/explorer + - BridgeHubPolkadot (see `bridgeKusamaMessages.MessagesReceived`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8945#/explorer + - AssetHubPolkadot (see `xcmpQueue.Success`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9010#/explorer + - BridgeHubKusama (see `bridgePolkadotMessages.MessagesDelivered`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8943#/explorer diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs index 14792067e43..8a33279cd2b 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs +++ b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs @@ -52,6 +52,8 @@ use xcm_executor::XcmExecutor; // Re-export test_case from assets pub use asset_test_utils::include_teleports_for_native_asset_works; +// Re-export +pub use parachains_runtimes_test_utils::change_storage_constant_by_governance_works; /// Test-case makes sure that `Runtime` can process bridging initialize via governance-like call pub fn initialize_bridge_by_governance_works( @@ -113,76 +115,6 @@ pub fn initialize_bridge_by_governance_works( }) } -/// Test-case makes sure that `Runtime` can change storage constant via governance-like call -pub fn change_storage_constant_by_governance_works( - collator_session_key: CollatorSessionKeys, - runtime_para_id: u32, - runtime_call_encode: Box) -> Vec>, - storage_constant_key_value: fn() -> (Vec, StorageConstantType), - new_storage_constant_value: fn(&StorageConstantType) -> StorageConstantType, -) where - Runtime: frame_system::Config - + pallet_balances::Config - + pallet_session::Config - + pallet_xcm::Config - + parachain_info::Config - + pallet_collator_selection::Config - + cumulus_pallet_dmp_queue::Config - + cumulus_pallet_parachain_system::Config, - ValidatorIdOf: From>, - StorageConstant: Get, - StorageConstantType: Encode + PartialEq + std::fmt::Debug, -{ - ExtBuilder::::default() - .with_collators(collator_session_key.collators()) - .with_session_keys(collator_session_key.session_keys()) - .with_para_id(runtime_para_id.into()) - .with_tracing() - .build() - .execute_with(|| { - let (storage_constant_key, storage_constant_init_value): ( - Vec, - StorageConstantType, - ) = storage_constant_key_value(); - - // check delivery reward constant before (not stored yet, just as default value is used) - assert_eq!(StorageConstant::get(), storage_constant_init_value); - assert_eq!(sp_io::storage::get(&storage_constant_key), None); - - let new_storage_constant_value = - new_storage_constant_value(&storage_constant_init_value); - assert_ne!(new_storage_constant_value, storage_constant_init_value); - - // encode `set_storage` call - let set_storage_call = - runtime_call_encode(frame_system::Call::::set_storage { - items: vec![( - storage_constant_key.clone(), - new_storage_constant_value.encode(), - )], - }); - - // estimate - storing just 1 value - use frame_system::WeightInfo; - let require_weight_at_most = - ::SystemWeightInfo::set_storage(1); - - // execute XCM with Transact to `set_storage` as governance does - assert_ok!(RuntimeHelper::::execute_as_governance( - set_storage_call, - require_weight_at_most - ) - .ensure_complete()); - - // check delivery reward constant after (stored) - assert_eq!(StorageConstant::get(), new_storage_constant_value); - assert_eq!( - sp_io::storage::get(&storage_constant_key), - Some(new_storage_constant_value.encode().into()) - ); - }) -} - /// Test-case makes sure that `Runtime` can handle xcm `ExportMessage`: /// Checks if received XCM messages is correctly added to the message outbound queue for delivery. /// For SystemParachains we expect unpaid execution. @@ -933,7 +865,7 @@ pub mod test_data { ); /// Simulates `HaulBlobExporter` and all its wrapping and captures generated plain bytes, - /// which are transfered over bridge. + /// which are transferred over bridge. pub(crate) fn simulate_message_exporter_on_bridged_chain< SourceNetwork: Get, DestinationNetwork: Get, diff --git a/parachains/runtimes/test-utils/src/lib.rs b/parachains/runtimes/test-utils/src/lib.rs index 8ff85438b3f..10fbffcf0e0 100644 --- a/parachains/runtimes/test-utils/src/lib.rs +++ b/parachains/runtimes/test-utils/src/lib.rs @@ -20,8 +20,10 @@ use cumulus_primitives_core::{AbridgedHrmpChannel, ParaId, PersistedValidationDa use cumulus_primitives_parachain_inherent::ParachainInherentData; use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; use frame_support::{ + assert_ok, dispatch::{DispatchResult, RawOrigin, UnfilteredDispatchable}, inherent::{InherentData, ProvideInherent}, + pallet_prelude::Get, traits::{GenesisBuild, OriginTrait}, weights::Weight, }; @@ -327,7 +329,6 @@ pub enum XcmReceivedFrom { impl RuntimeHelper { pub fn xcm_max_weight(from: XcmReceivedFrom) -> Weight { - use frame_support::traits::Get; match from { XcmReceivedFrom::Parent => ParachainSystem::ReservedDmpWeight::get(), XcmReceivedFrom::Sibling => ParachainSystem::ReservedXcmpWeight::get(), @@ -478,3 +479,73 @@ impl } } } + +/// Test-case makes sure that `Runtime` can change storage constant via governance-like call +pub fn change_storage_constant_by_governance_works( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + runtime_call_encode: Box) -> Vec>, + storage_constant_key_value: fn() -> (Vec, StorageConstantType), + new_storage_constant_value: fn(&StorageConstantType) -> StorageConstantType, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_dmp_queue::Config + + cumulus_pallet_parachain_system::Config, + ValidatorIdOf: From>, + StorageConstant: Get, + StorageConstantType: Encode + PartialEq + std::fmt::Debug, +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + let (storage_constant_key, storage_constant_init_value): ( + Vec, + StorageConstantType, + ) = storage_constant_key_value(); + + // check constant before (not stored yet, just as default value is used) + assert_eq!(StorageConstant::get(), storage_constant_init_value); + assert_eq!(sp_io::storage::get(&storage_constant_key), None); + + let new_storage_constant_value = + new_storage_constant_value(&storage_constant_init_value); + assert_ne!(new_storage_constant_value, storage_constant_init_value); + + // encode `set_storage` call + let set_storage_call = + runtime_call_encode(frame_system::Call::::set_storage { + items: vec![( + storage_constant_key.clone(), + new_storage_constant_value.encode(), + )], + }); + + // estimate - storing just 1 value + use frame_system::WeightInfo; + let require_weight_at_most = + ::SystemWeightInfo::set_storage(1); + + // execute XCM with Transact to `set_storage` as governance does + assert_ok!(RuntimeHelper::::execute_as_governance( + set_storage_call, + require_weight_at_most + ) + .ensure_complete()); + + // check constant after (stored) + assert_eq!(StorageConstant::get(), new_storage_constant_value); + assert_eq!( + sp_io::storage::get(&storage_constant_key), + Some(new_storage_constant_value.encode().into()) + ); + }) +} diff --git a/scripts/bridges_kusama_polkadot.sh b/scripts/bridges_kusama_polkadot.sh new file mode 100755 index 00000000000..fef6634e645 --- /dev/null +++ b/scripts/bridges_kusama_polkadot.sh @@ -0,0 +1,112 @@ +#!/bin/bash + +# import common functions +source "$(dirname "$0")"/bridges_rococo_wococo.sh "import" + +# Address: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY +# AccountId: [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] +ASSET_HUB_KUSAMA_ACCOUNT_SEED_FOR_LOCAL="//Alice" +# Address: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY +# AccountId: [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] +ASSET_HUB_POLKADOT_ACCOUNT_ADDRESS_FOR_LOCAL="5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" + +# SovereignAccount for `MultiLocation { parents: 2, interior: X2(GlobalConsensus(Rococo), Parachain(1000)) }` => 5DLdHR78ujzS93zCVeyZB1qRFjBCdMnJwnpSBSRZ6jMX8R5y +# +# use sp_core::crypto::Ss58Codec; +# println!("{}", +# frame_support::sp_runtime::AccountId32::new( +# GlobalConsensusParachainConvertsFor::::convert_ref( +# MultiLocation { parents: 2, interior: X2(GlobalConsensus(Kusama), Parachain(1000)) }).unwrap() +# ).to_ss58check_with_version(42_u16.into()) +# ); +KUSAMA_STATEMINE_1000_SOVEREIGN_ACCOUNT="5DLdHR78ujzS93zCVeyZB1qRFjBCdMnJwnpSBSRZ6jMX8R5y" + +function init_ksm_dot() { + ensure_relayer + + RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay init-bridge kusama-to-bridge-hub-polkadot \ + --source-host localhost \ + --source-port 9942 \ + --source-version-mode Auto \ + --target-host localhost \ + --target-port 8945 \ + --target-version-mode Auto \ + --target-signer //Bob +} + +function init_dot_ksm() { + ensure_relayer + + RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay init-bridge polkadot-to-bridge-hub-kusama \ + --source-host localhost \ + --source-port 9945 \ + --source-version-mode Auto \ + --target-host localhost \ + --target-port 8943 \ + --target-version-mode Auto \ + --target-signer //Bob +} + +function run_relay() { + ensure_relayer + +RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay relay-headers-and-messages bridge-hub-kusama-bridge-hub-polkadot \ + --kusama-host localhost \ + --kusama-port 9942 \ + --kusama-version-mode Auto \ + --bridge-hub-kusama-host localhost \ + --bridge-hub-kusama-port 8943 \ + --bridge-hub-kusama-version-mode Auto \ + --bridge-hub-kusama-signer //Charlie \ + --polkadot-headers-to-bridge-hub-kusama-signer //Bob \ + --polkadot-parachains-to-bridge-hub-kusama-signer //Bob \ + --bridge-hub-kusama-transactions-mortality 4 \ + --polkadot-host localhost \ + --polkadot-port 9945 \ + --polkadot-version-mode Auto \ + --bridge-hub-polkadot-host localhost \ + --bridge-hub-polkadot-port 8945 \ + --bridge-hub-polkadot-version-mode Auto \ + --bridge-hub-polkadot-signer //Charlie \ + --kusama-headers-to-bridge-hub-polkadot-signer //Bob \ + --kusama-parachains-to-bridge-hub-polkadot-signer //Bob \ + --bridge-hub-polkadot-transactions-mortality 4 \ + --lane 00000000 +} + +case "$1" in + run-relay) + init_ksm_dot + init_dot_ksm + run_relay + ;; + transfer-asset-from-asset-hub-kusama-local) + ensure_polkadot_js_api + transfer_asset_via_bridge \ + "ws://127.0.0.1:9910" \ + "$ASSET_HUB_KUSAMA_ACCOUNT_SEED_FOR_LOCAL" \ + "$ASSET_HUB_POLKADOT_ACCOUNT_ADDRESS_FOR_LOCAL" \ + "Polkadot" + ;; + drip) + transfer_balance \ + "ws://127.0.0.1:9010" \ + "//Alice" \ + "$KUSAMA_STATEMINE_1000_SOVEREIGN_ACCOUNT" \ + $((1000000000 + 50000000000 * 20)) + ;; + stop) + pkill -f polkadot + pkill -f parachain + ;; + *) + echo "A command is require. Supported commands for: + Local (zombienet) run: + - run-relay + - transfer-asset-from-asset-hub-kusama-local"; + exit 1 + ;; +esac diff --git a/zombienet/bridge-hubs/bridge_hub_kusama_local_network.toml b/zombienet/bridge-hubs/bridge_hub_kusama_local_network.toml new file mode 100644 index 00000000000..7f753d6dd50 --- /dev/null +++ b/zombienet/bridge-hubs/bridge_hub_kusama_local_network.toml @@ -0,0 +1,107 @@ +[settings] +node_spawn_timeout = 240 + +[relaychain] +default_command = "{{POLKADOT_BINARY_PATH}}" +default_args = [ "-lparachain=debug,xcm=trace" ] +chain = "kusama-local" + + [[relaychain.nodes]] + name = "alice-validator" + validator = true + rpc_port = 9932 + ws_port = 9942 + extra_args = ["--no-mdns --bootnodes {{'bob-validator'|zombie('multiAddress')}}"] + balance = 2000000000000 + + [[relaychain.nodes]] + name = "bob-validator" + validator = true + rpc_port = 9933 + ws_port = 9943 + extra_args = ["--no-mdns --bootnodes {{'alice-validator'|zombie('multiAddress')}}"] + balance = 2000000000000 + + [[relaychain.nodes]] + name = "charlie-validator" + validator = true + rpc_port = 9934 + ws_port = 9944 + extra_args = ["--no-mdns --bootnodes {{'alice-validator'|zombie('multiAddress')}}"] + balance = 2000000000000 + +[[parachains]] +id = 1002 +chain = "bridge-hub-kusama-local" +cumulus_based = true + + # run alice as parachain collator + [[parachains.collators]] + name = "alice-collator" + validator = true + command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + rpc_port = 8933 + ws_port = 8943 + args = [ + "-lparachain=debug,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace", + ] + extra_args = [ + "--force-authoring", "--no-mdns", "--bootnodes {{'bob-collator'|zombie('multiAddress')}}", + "-- --port 41333 --rpc-port 48933 --ws-port 48943 --no-mdns", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" + ] + + # run bob as parachain collator + [[parachains.collators]] + name = "bob-collator" + validator = true + command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + rpc_port = 8934 + ws_port = 8944 + args = [ + "-lparachain=trace,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace", + ] + extra_args = [ + "--force-authoring", "--no-mdns", "--bootnodes {{'alice-collator'|zombie('multiAddress')}}", + "-- --port 41334 --rpc-port 48934 --ws-port 48944 --no-mdns", "--bootnodes {{'bob-validator'|zombie('multiAddress')}}" + ] + +[[parachains]] +id = 1000 +chain = "asset-hub-kusama-local" +cumulus_based = true + + [[parachains.collators]] + name = "asset-hub-kusama-collator1" + rpc_port = 9911 + ws_port = 9910 + command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_KUSAMA}}" + args = [ + "-lparachain=debug,xcm=trace,runtime::bridge-transfer=trace", + ] + extra_args = [ + "--no-mdns", "--bootnodes {{'asset-hub-kusama-collator2'|zombie('multiAddress')}}", + "-- --port 51333 --rpc-port 58933 --ws-port 58943 --no-mdns", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" + ] + + [[parachains.collators]] + name = "asset-hub-kusama-collator2" + command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_KUSAMA}}" + args = [ + "-lparachain=debug,xcm=trace,runtime::bridge-transfer=trace", + ] + extra_args = [ + "--no-mdns", "--bootnodes {{'asset-hub-kusama-collator1'|zombie('multiAddress')}}", + "-- --port 51433 --rpc-port 58833 --ws-port 58843 --no-mdns", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" + ] + +[[hrmp_channels]] +sender = 1000 +recipient = 1002 +max_capacity = 4 +max_message_size = 524288 + +[[hrmp_channels]] +sender = 1002 +recipient = 1000 +max_capacity = 4 +max_message_size = 524288 diff --git a/zombienet/bridge-hubs/bridge_hub_polkadot_local_network.toml b/zombienet/bridge-hubs/bridge_hub_polkadot_local_network.toml new file mode 100644 index 00000000000..320456a2565 --- /dev/null +++ b/zombienet/bridge-hubs/bridge_hub_polkadot_local_network.toml @@ -0,0 +1,107 @@ +[settings] +node_spawn_timeout = 240 + +[relaychain] +default_command = "{{POLKADOT_BINARY_PATH}}" +default_args = [ "-lparachain=debug,xcm=trace" ] +chain = "polkadot-local" + + [[relaychain.nodes]] + name = "alice-validator-dot" + validator = true + rpc_port = 9935 + ws_port = 9945 + extra_args = ["--no-mdns --bootnodes {{'bob-validator-dot'|zombie('multiAddress')}}"] + balance = 2000000000000 + + [[relaychain.nodes]] + name = "bob-validator-dot" + validator = true + rpc_port = 9936 + ws_port = 9946 + extra_args = ["--no-mdns --bootnodes {{'alice-validator-dot'|zombie('multiAddress')}}"] + balance = 2000000000000 + + [[relaychain.nodes]] + name = "charlie-validator-dot" + validator = true + rpc_port = 9937 + ws_port = 9947 + extra_args = ["--no-mdns --bootnodes {{'alice-validator-dot'|zombie('multiAddress')}}"] + balance = 2000000000000 + +[[parachains]] +id = 1002 +chain = "bridge-hub-polkadot-local" +cumulus_based = true + + # run alice as parachain collator + [[parachains.collators]] + name = "alice-collator-dot" + validator = true + command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + rpc_port = 8935 + ws_port = 8945 + args = [ + "-lparachain=debug,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace", + ] + extra_args = [ + "--force-authoring", "--no-mdns", "--bootnodes {{'bob-collator-dot'|zombie('multiAddress')}}", + "-- --port 41335 --rpc-port 48935 --ws-port 48945 --no-mdns", "--bootnodes {{'alice-validator-dot'|zombie('multiAddress')}}" + ] + + # run bob as parachain collator + [[parachains.collators]] + name = "bob-collator-dot" + validator = true + command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + rpc_port = 8936 + ws_port = 8946 + args = [ + "-lparachain=trace,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace", + ] + extra_args = [ + "--force-authoring", "--no-mdns", "--bootnodes {{'alice-collator-dot'|zombie('multiAddress')}}", + "-- --port 41336 --rpc-port 48936 --ws-port 48946 --no-mdns", "--bootnodes {{'bob-validator-dot'|zombie('multiAddress')}}" + ] + +[[parachains]] +id = 1000 +chain = "asset-hub-polkadot-local" +cumulus_based = true + + [[parachains.collators]] + name = "asset-hub-polkadot-collator1" + rpc_port = 9011 + ws_port = 9010 + command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_POLKADOT}}" + args = [ + "-lparachain=debug,xcm=trace,runtime::bridge-transfer=trace", + ] + extra_args = [ + "--no-mdns", "--bootnodes {{'asset-hub-polkadot-collator2'|zombie('multiAddress')}}", + "-- --port 31333 --rpc-port 38933 --ws-port 38943 --no-mdns", "--bootnodes {{'alice-validator-dot'|zombie('multiAddress')}}" + ] + + [[parachains.collators]] + name = "asset-hub-polkadot-collator2" + command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ASSET_HUB_POLKADOT}}" + args = [ + "-lparachain=debug,xcm=trace,runtime::bridge-transfer=trace", + ] + extra_args = [ + "--no-mdns", "--bootnodes {{'asset-hub-polkadot-collator1'|zombie('multiAddress')}}", + "-- --port 31433 --rpc-port 38833 --ws-port 38843 --no-mdns", "--bootnodes {{'alice-validator-dot'|zombie('multiAddress')}}" + ] + +[[hrmp_channels]] +sender = 1000 +recipient = 1002 +max_capacity = 4 +max_message_size = 524288 + +[[hrmp_channels]] +sender = 1002 +recipient = 1000 +max_capacity = 4 +max_message_size = 524288