diff --git a/Cargo.lock b/Cargo.lock index 4a81c2221e1a6..1f9a68787bf2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -722,6 +722,7 @@ version = "0.9.420" dependencies = [ "asset-test-utils", "assets-common", + "bp-asset-hub-kusama", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -761,6 +762,7 @@ dependencies = [ "pallet-utility", "pallet-xcm", "pallet-xcm-benchmarks", + "pallet-xcm-bridge-hub-router", "parachain-info", "parachains-common", "parity-scale-codec", @@ -818,6 +820,7 @@ version = "0.9.420" dependencies = [ "asset-test-utils", "assets-common", + "bp-asset-hub-polkadot", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -853,6 +856,7 @@ dependencies = [ "pallet-utility", "pallet-xcm", "pallet-xcm-benchmarks", + "pallet-xcm-bridge-hub-router", "parachain-info", "parachains-common", "parity-scale-codec", @@ -996,6 +1000,7 @@ dependencies = [ "pallet-collator-selection", "pallet-session", "pallet-xcm", + "pallet-xcm-bridge-hub-router", "parachain-info", "parachains-common", "parachains-runtimes-test-utils", @@ -3626,6 +3631,7 @@ dependencies = [ name = "cumulus-pallet-xcmp-queue" version = "0.1.0" dependencies = [ + "bp-xcm-bridge-hub-router", "cumulus-pallet-parachain-system", "cumulus-primitives-core", "frame-benchmarking", diff --git a/cumulus/pallets/xcmp-queue/Cargo.toml b/cumulus/pallets/xcmp-queue/Cargo.toml index de4ad61de9e09..0e6a34ecfe698 100644 --- a/cumulus/pallets/xcmp-queue/Cargo.toml +++ b/cumulus/pallets/xcmp-queue/Cargo.toml @@ -28,6 +28,9 @@ cumulus-primitives-core = { path = "../../primitives/core", default-features = f # Optional import for benchmarking frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true} +# Bridges +bp-xcm-bridge-hub-router = { path = "../../bridges/primitives/xcm-bridge-hub-router", default-features = false, optional = true } + [dev-dependencies] # Substrate @@ -43,6 +46,7 @@ cumulus-pallet-parachain-system = { path = "../parachain-system" } [features] default = [ "std" ] std = [ + "bp-xcm-bridge-hub-router/std", "codec/std", "cumulus-primitives-core/std", "frame-benchmarking?/std", @@ -77,3 +81,4 @@ try-runtime = [ "polkadot-runtime-common/try-runtime", "sp-runtime/try-runtime", ] +bridging = [ "bp-xcm-bridge-hub-router" ] diff --git a/cumulus/pallets/xcmp-queue/src/bridging.rs b/cumulus/pallets/xcmp-queue/src/bridging.rs new file mode 100644 index 0000000000000..bd36ded8d6282 --- /dev/null +++ b/cumulus/pallets/xcmp-queue/src/bridging.rs @@ -0,0 +1,116 @@ +// Copyright (C) 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::pallet; +use cumulus_primitives_core::ParaId; +use frame_support::pallet_prelude::Get; + +/// Adapter implementation for `bp_xcm_bridge_hub_router::XcmChannelStatusProvider` which checks +/// both `OutboundXcmpStatus` and `InboundXcmpStatus` for defined `ParaId` if any of those is +/// suspended. +pub struct InboundAndOutboundXcmpChannelCongestionStatusProvider( + sp_std::marker::PhantomData<(SiblingBridgeHubParaId, Runtime)>, +); +impl, Runtime: crate::Config> + bp_xcm_bridge_hub_router::XcmChannelStatusProvider + for InboundAndOutboundXcmpChannelCongestionStatusProvider +{ + fn is_congested() -> bool { + // if the outbound channel with recipient is suspended, it means that one of further + // bridge queues (e.g. bridge queue between two bridge hubs) is overloaded, so we shall + // take larger fee for our outbound messages + let sibling_bridge_hub_id: ParaId = SiblingBridgeHubParaId::get(); + let outbound_channels = pallet::OutboundXcmpStatus::::get(); + let outbound_channel = + outbound_channels.iter().find(|c| c.recipient == sibling_bridge_hub_id); + let is_outbound_channel_suspended = + outbound_channel.map(|c| c.is_suspended()).unwrap_or(false); + if is_outbound_channel_suspended { + return true + } + + // if the inbound channel with recipient is suspended, it means that we are unable to + // receive congestion reports from the bridge hub. So we assume the bridge pipeline is + // congested too + let inbound_channels = pallet::InboundXcmpStatus::::get(); + let inbound_channel = inbound_channels.iter().find(|c| c.sender == sibling_bridge_hub_id); + let is_inbound_channel_suspended = + inbound_channel.map(|c| c.is_suspended()).unwrap_or(false); + if is_inbound_channel_suspended { + return true + } + + // TODO: https://github.com/paritytech/cumulus/pull/2342 - once this PR is merged, we may + // remove the following code + // + // if the outbound channel has at least `N` pages enqueued, let's assume it is congested. + // Normally, the chain with a few opened HRMP channels, will "send" pages at every block. + // Having `N` pages means that for last `N` blocks we either have not sent any messages, + // or have sent signals. + const MAX_OUTBOUND_PAGES_BEFORE_CONGESTION: u16 = 4; + let is_outbound_channel_congested = outbound_channel.map(|c| c.queued_pages()).unwrap_or(0); + is_outbound_channel_congested > MAX_OUTBOUND_PAGES_BEFORE_CONGESTION + } +} + +/// Adapter implementation for `bp_xcm_bridge_hub_router::XcmChannelStatusProvider` which checks +/// only `OutboundXcmpStatus` for defined `SiblingParaId` if is suspended. +pub struct OutboundXcmpChannelCongestionStatusProvider( + sp_std::marker::PhantomData<(SiblingBridgeHubParaId, Runtime)>, +); +impl, Runtime: crate::Config> + bp_xcm_bridge_hub_router::XcmChannelStatusProvider + for OutboundXcmpChannelCongestionStatusProvider +{ + fn is_congested() -> bool { + // let's find the channel with the sibling parachain + let sibling_para_id: cumulus_primitives_core::ParaId = SiblingParaId::get(); + let outbound_channels = pallet::OutboundXcmpStatus::::get(); + let channel_with_sibling_parachain = + outbound_channels.iter().find(|c| c.recipient == sibling_para_id); + + // no channel => it is empty, so not congested + let channel_with_sibling_parachain = match channel_with_sibling_parachain { + Some(channel_with_sibling_parachain) => channel_with_sibling_parachain, + None => return false, + }; + + // suspended channel => it is congested + if channel_with_sibling_parachain.is_suspended() { + return true + } + + // TODO: the following restriction is arguable, we may live without that, assuming that + // there can't be more than some `N` messages queued at the bridge queue (at the source BH) + // AND before accepting next (or next-after-next) delivery transaction, we'll receive the + // suspension signal from the target parachain and stop accepting delivery transactions + + // it takes some time for target parachain to suspend inbound channel with the target BH and + // during that we will keep accepting new message delivery transactions. Let's also reject + // new deliveries if there are too many "pages" (concatenated XCM messages) in the target BH + // -> target parachain queue. + const MAX_QUEUED_PAGES_BEFORE_DEACTIVATION: u16 = 4; + if channel_with_sibling_parachain.queued_pages() > MAX_QUEUED_PAGES_BEFORE_DEACTIVATION { + return true + } + + true + } +} + +#[cfg(feature = "runtime-benchmarks")] +pub fn suspend_channel_for_benchmarks(target: ParaId) { + pallet::Pallet::::suspend_channel(target) +} diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs index 1cb92f5951869..db9c433b52d35 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -35,6 +35,8 @@ mod tests; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; +#[cfg(feature = "bridging")] +pub mod bridging; pub mod weights; pub use weights::WeightInfo; @@ -400,6 +402,13 @@ pub struct InboundChannelDetails { message_metadata: Vec<(RelayBlockNumber, XcmpMessageFormat)>, } +impl InboundChannelDetails { + #[cfg(feature = "bridging")] + pub(crate) fn is_suspended(&self) -> bool { + self.state == InboundState::Suspended + } +} + /// Struct containing detailed information about the outbound channel. #[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] pub struct OutboundChannelDetails { @@ -435,6 +444,16 @@ impl OutboundChannelDetails { self.state = OutboundState::Suspended; self } + + #[cfg(feature = "bridging")] + pub(crate) fn is_suspended(&self) -> bool { + self.state == OutboundState::Suspended + } + + #[cfg(feature = "bridging")] + pub(crate) fn queued_pages(&self) -> u16 { + self.last_index.saturating_sub(self.first_index) + } } #[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] diff --git a/cumulus/parachains/common/src/xcm_config.rs b/cumulus/parachains/common/src/xcm_config.rs index aa1183f5d921e..984cce297adf6 100644 --- a/cumulus/parachains/common/src/xcm_config.rs +++ b/cumulus/parachains/common/src/xcm_config.rs @@ -14,16 +14,22 @@ // limitations under the License. use crate::impls::AccountIdOf; +use codec::Decode; use core::marker::PhantomData; use frame_support::{ - traits::{fungibles::Inspect, tokens::ConversionToAssetBalance, ContainsPair}, + ensure, + traits::{ + fungibles::Inspect, tokens::ConversionToAssetBalance, Contains, ContainsPair, + ProcessMessageError, + }, weights::Weight, DefaultNoBound, }; use log; use sp_runtime::traits::Get; -use xcm::latest::prelude::*; -use xcm_builder::ExporterFor; +use xcm::{latest::prelude::*, DoubleEncoded}; +use xcm_builder::{CreateMatcher, ExporterFor, MatchXcm}; +use xcm_executor::traits::ShouldExecute; /// A `ChargeFeeInFungibles` implementation that converts the output of /// a given WeightToFee implementation an amount charged in @@ -171,6 +177,59 @@ impl< } } +/// Allows execution from `origin` if it is contained in `AllowedOrigin` +/// and if it is just a straight `Transact` which contains `AllowedCall`. +pub struct AllowUnpaidTransactsFrom( + sp_std::marker::PhantomData<(RuntimeCall, AllowedCall, AllowedOrigin)>, +); +impl< + RuntimeCall: Decode, + AllowedCall: Contains, + AllowedOrigin: Contains, + > ShouldExecute for AllowUnpaidTransactsFrom +{ + fn should_execute( + origin: &MultiLocation, + instructions: &mut [Instruction], + max_weight: Weight, + _properties: &mut xcm_executor::traits::Properties, + ) -> Result<(), ProcessMessageError> { + log::trace!( + target: "xcm::barriers", + "AllowUnpaidTransactFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}", + origin, instructions, max_weight, _properties, + ); + + // we only allow from configured origins + ensure!(AllowedOrigin::contains(origin), ProcessMessageError::Unsupported); + + // we expect an XCM program with single `Transact` call + instructions + .matcher() + .assert_remaining_insts(1)? + .match_next_inst(|inst| match inst { + Transact { origin_kind: OriginKind::Xcm, call: encoded_call, .. } => { + // this is a hack - don't know if there's a way to do that properly + // or else we can simply allow all calls + let mut decoded_call = DoubleEncoded::::from(encoded_call.clone()); + ensure!( + AllowedCall::contains( + decoded_call + .ensure_decoded() + .map_err(|_| ProcessMessageError::BadFormat)? + ), + ProcessMessageError::BadFormat, + ); + + Ok(()) + }, + _ => Err(ProcessMessageError::BadFormat), + })?; + + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-kusama/Cargo.toml index 53555499842fd..6a9f7c7f8a815 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/Cargo.toml @@ -71,7 +71,7 @@ cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-fea cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook",] } cumulus-pallet-session-benchmarking = { path = "../../../../pallets/session-benchmarking", default-features = false} cumulus-pallet-xcm = { path = "../../../../pallets/xcm", default-features = false } -cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } +cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false, features = ["bridging"] } cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } @@ -79,6 +79,10 @@ parachain-info = { path = "../../../pallets/parachain-info", default-features = parachains-common = { path = "../../../common", default-features = false } assets-common = { path = "../common", default-features = false } +# Bridges +pallet-xcm-bridge-hub-router = { path = "../../../../bridges/modules/xcm-bridge-hub-router", default-features = false } +bp-asset-hub-kusama = { path = "../../../../bridges/primitives/chain-asset-hub-kusama", default-features = false } + [dev-dependencies] asset-test-utils = { path = "../test-utils" } @@ -117,6 +121,7 @@ runtime-benchmarks = [ "pallet-uniques/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", + "pallet-xcm-bridge-hub-router/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", @@ -151,6 +156,7 @@ try-runtime = [ "pallet-transaction-payment/try-runtime", "pallet-uniques/try-runtime", "pallet-utility/try-runtime", + "pallet-xcm-bridge-hub-router/try-runtime", "pallet-xcm/try-runtime", "parachain-info/try-runtime", "polkadot-runtime-common/try-runtime", @@ -158,6 +164,7 @@ try-runtime = [ ] std = [ "assets-common/std", + "bp-asset-hub-kusama/std", "codec/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", @@ -196,6 +203,7 @@ std = [ "pallet-uniques/std", "pallet-utility/std", "pallet-xcm-benchmarks?/std", + "pallet-xcm-bridge-hub-router/std", "pallet-xcm/std", "parachain-info/std", "parachains-common/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs index f8ebdefe6870d..44d6d81edd769 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs @@ -815,6 +815,38 @@ impl pallet_nfts::Config for Runtime { type Helper = (); } +/// XCM router instance to BridgeHub with bridging capabilities for `Polkadot` global consensus with +/// dynamic fees and back-pressure. +pub type ToPolkadotXcmRouterInstance = pallet_assets::Instance1; +impl pallet_xcm_bridge_hub_router::Config for Runtime { + type WeightInfo = weights::pallet_xcm_bridge_hub_router::WeightInfo; + + type UniversalLocation = xcm_config::UniversalLocation; + type BridgedNetworkId = xcm_config::bridging::PolkadotNetwork; + type Bridges = xcm_config::bridging::FilteredNetworkExportTable; + + #[cfg(not(feature = "runtime-benchmarks"))] + type BridgeHubOrigin = + EnsureXcm>; + #[cfg(feature = "runtime-benchmarks")] + type BridgeHubOrigin = EitherOfDiverse< + // for running benchmarks + EnsureRoot, + // for running tests with `--feature runtime-benchmarks` + EnsureXcm>, + >; + + type ToBridgeHubSender = XcmpQueue; + type WithBridgeHubChannel = + cumulus_pallet_xcmp_queue::bridging::InboundAndOutboundXcmpChannelCongestionStatusProvider< + xcm_config::bridging::BridgeHubKusamaParaId, + Runtime, + >; + + type ByteFee = xcm_config::bridging::XcmBridgeHubRouterByteFee; + type FeeAsset = xcm_config::bridging::XcmBridgeHubRouterFeeAssetId; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime @@ -851,6 +883,9 @@ construct_runtime!( Multisig: pallet_multisig::{Pallet, Call, Storage, Event} = 41, Proxy: pallet_proxy::{Pallet, Call, Storage, Event} = 42, + // Bridge utilities. + ToPolkadotXcmRouter: pallet_xcm_bridge_hub_router::::{Pallet, Storage, Call} = 43, + // The main stage. Assets: pallet_assets::::{Pallet, Call, Storage, Event} = 50, Uniques: pallet_uniques::{Pallet, Call, Storage, Event} = 51, @@ -924,6 +959,7 @@ mod benches { [pallet_timestamp, Timestamp] [pallet_collator_selection, CollatorSelection] [cumulus_pallet_xcmp_queue, XcmpQueue] + [pallet_xcm_bridge_hub_router, XcmBridgeHubRouterBench] // XCM [pallet_xcm, PolkadotXcm] // NOTE: Make sure you point to the individual modules below. @@ -1163,6 +1199,7 @@ impl_runtime_apis! { use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; + use pallet_xcm_bridge_hub_router::benchmarking::Pallet as XcmBridgeHubRouterBench; // This is defined once again in dispatch_benchmark, because list_benchmarks! // and add_benchmarks! are macros exported by define_benchmarks! macros and those types @@ -1206,6 +1243,25 @@ impl_runtime_apis! { use cumulus_pallet_session_benchmarking::Pallet as SessionBench; impl cumulus_pallet_session_benchmarking::Config for Runtime {} + use pallet_xcm_bridge_hub_router::benchmarking::{ + Pallet as XcmBridgeHubRouterBench, + Config as XcmBridgeHubRouterConfig, + }; + + impl XcmBridgeHubRouterConfig for Runtime { + fn make_congested() { + cumulus_pallet_xcmp_queue::bridging::suspend_channel_for_benchmarks::( + xcm_config::bridging::BridgeHubKusamaParaId::get().into() + ); + } + fn ensure_bridged_target_destination() -> MultiLocation { + ParachainSystem::open_outbound_hrmp_channel_for_benchmarks( + xcm_config::bridging::BridgeHubKusamaParaId::get().into() + ); + xcm_config::bridging::AssetHubPolkadot::get() + } + } + use xcm::latest::prelude::*; use xcm_config::{KsmLocation, MaxAssetsIntoHolding}; use pallet_xcm_benchmarks::asset_instance_from; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/mod.rs index 281c013b3372f..a857dc6206220 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/mod.rs @@ -33,6 +33,7 @@ pub mod pallet_timestamp; pub mod pallet_uniques; pub mod pallet_utility; pub mod pallet_xcm; +pub mod pallet_xcm_bridge_hub_router; pub mod paritydb_weights; pub mod rocksdb_weights; pub mod xcm; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_xcm_bridge_hub_router.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_xcm_bridge_hub_router.rs new file mode 100644 index 0000000000000..8fc21a35bd61f --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/weights/pallet_xcm_bridge_hub_router.rs @@ -0,0 +1,118 @@ +// 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_xcm_bridge_hub_router` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-08-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-aahe6cbd-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! 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=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_xcm_bridge_hub_router +// --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_xcm_bridge_hub_router`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm_bridge_hub_router::WeightInfo for WeightInfo { + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::InboundXcmpStatus` (r:1 w:0) + /// Proof: `XcmpQueue::InboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ToPolkadotXcmRouter::Bridge` (r:1 w:1) + /// Proof: `ToPolkadotXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) + fn on_initialize_when_non_congested() -> Weight { + // Proof Size summary in bytes: + // Measured: `159` + // Estimated: `1644` + // Minimum execution time: 9_957_000 picoseconds. + Weight::from_parts(10_409_000, 0) + .saturating_add(Weight::from_parts(0, 1644)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn on_initialize_when_congested() -> Weight { + // Proof Size summary in bytes: + // Measured: `111` + // Estimated: `1596` + // Minimum execution time: 3_274_000 picoseconds. + Weight::from_parts(3_445_000, 0) + .saturating_add(Weight::from_parts(0, 1596)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `ToPolkadotXcmRouter::Bridge` (r:1 w:1) + /// Proof: `ToPolkadotXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) + fn report_bridge_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `83` + // Estimated: `1502` + // Minimum execution time: 11_282_000 picoseconds. + Weight::from_parts(11_712_000, 0) + .saturating_add(Weight::from_parts(0, 1502)) + .saturating_add(T::DbWeight::get().reads(1)) + .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 KEY `0x0973fe64c85043ba1c965cbc38eb63c7` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x0973fe64c85043ba1c965cbc38eb63c7` (r:1 w:0) + /// Storage: `ToPolkadotXcmRouter::Bridge` (r:1 w:1) + /// Proof: `ToPolkadotXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0) + /// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1) + /// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn send_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `355` + // Estimated: `3820` + // Minimum execution time: 49_615_000 picoseconds. + Weight::from_parts(50_859_000, 0) + .saturating_add(Weight::from_parts(0, 3820)) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs index 99e46a1b4fe6f..183940956f523 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs @@ -14,11 +14,11 @@ // limitations under the License. use super::{ - AccountId, AllPalletsWithSystem, Assets, Authorship, Balance, Balances, ParachainInfo, - ParachainSystem, PolkadotXcm, PoolAssets, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, + AccountId, AllPalletsWithSystem, Assets, Authorship, Balance, Balances, ForeignAssets, + ForeignAssetsInstance, ParachainInfo, ParachainSystem, PolkadotXcm, PoolAssets, Runtime, + RuntimeCall, RuntimeEvent, RuntimeOrigin, ToPolkadotXcmRouter, TransactionByteFee, TrustBackedAssetsInstance, WeightToFee, XcmpQueue, }; -use crate::{ForeignAssets, ForeignAssetsInstance}; use assets_common::{ local_and_foreign_assets::MatchesLocalAndForeignAssetsMultiLocation, matching::{ @@ -31,7 +31,10 @@ use frame_support::{ }; use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; -use parachains_common::{impls::ToStakingPot, xcm_config::AssetFeeAsExistentialDepositMultiplier}; +use parachains_common::{ + impls::ToStakingPot, + xcm_config::{AllowUnpaidTransactsFrom, AssetFeeAsExistentialDepositMultiplier}, +}; use polkadot_parachain_primitives::primitives::Sibling; use sp_runtime::traits::ConvertInto; use xcm::latest::prelude::*; @@ -252,6 +255,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().all(|(k, _)| k.eq(&bridging::XcmBridgeHubRouterByteFee::key())) => + return true, + _ => (), + }; + matches!( call, RuntimeCall::PolkadotXcm(pallet_xcm::Call::force_xcm_version { .. }) | @@ -436,6 +447,8 @@ impl Contains for SafeCallFilter { pallet_uniques::Call::set_collection_max_supply { .. } | pallet_uniques::Call::set_price { .. } | pallet_uniques::Call::buy_item { .. } + ) | RuntimeCall::ToPolkadotXcmRouter( + pallet_xcm_bridge_hub_router::Call::report_bridge_status { .. } ) ) } @@ -458,6 +471,8 @@ pub type Barrier = TrailingSetTopicAsId< AllowExplicitUnpaidExecutionFrom, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, + // Allows Transacts with `report_bridge_status` from sibling BridgeHub. + bridging::AllowUnpaidStatusReportsFromSiblingBridgeHub, ), UniversalLocation, ConstU32<8>, @@ -544,6 +559,7 @@ impl xcm_executor::Config for XcmConfig { type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); + // TODO:check-parameter: change and assert in tests when (https://github.com/paritytech/polkadot/pull/7005) merged type FeeManager = (); type MessageExporter = (); type UniversalAliases = bridging::UniversalAliases; @@ -566,7 +582,12 @@ type LocalXcmRouter = ( /// The means for routing XCM messages which are not for local execution into the right message /// queues. -pub type XcmRouter = WithUniqueTopic<(LocalXcmRouter, bridging::BridgingXcmRouter)>; +pub type XcmRouter = WithUniqueTopic<( + LocalXcmRouter, + // Router which wraps and sends xcm to BridgeHub to be delivered to the Polkadot + // GlobalConsensus + ToPolkadotXcmRouter, +)>; #[cfg(feature = "runtime-benchmarks")] parameter_types! { @@ -665,7 +686,6 @@ pub mod bridging { use assets_common::{matching, matching::*}; use parachains_common::xcm_config::LocationFilter; use sp_std::collections::btree_set::BTreeSet; - use xcm_builder::UnpaidRemoteExporter; parameter_types! { pub BridgeHubKusamaParaId: u32 = 1002; @@ -675,7 +695,14 @@ pub mod bridging { pub AssetHubPolkadot: MultiLocation = MultiLocation::new(2, X2(GlobalConsensus(PolkadotNetwork::get()), Parachain(1000))); pub DotLocation: MultiLocation = MultiLocation::new(2, X1(GlobalConsensus(PolkadotNetwork::get()))); + /// Router expects payment with this `AssetId`. + /// (`AssetId` has to be aligned with `BridgeTable`) + pub XcmBridgeHubRouterFeeAssetId: AssetId = KsmLocation::get().into(); + /// Price per byte - can be adjusted via governance `set_storage` call. + pub storage XcmBridgeHubRouterByteFee: Balance = TransactionByteFee::get(); + /// Set up exporters configuration. + /// `Option` represents static "base fee" which is used for total delivery fee calculation. pub BridgeTable: sp_std::vec::Vec<(NetworkId, LocationFilter, MultiLocation, Option)> = sp_std::vec![ ( PolkadotNetwork::get(), @@ -684,7 +711,11 @@ pub mod bridging { .add_equals(AssetHubPolkadot::get().interior.split_global().expect("invalid configuration for AssetHubPolkadot").1), // and nothing else BridgeHubKusama::get(), - None + // base delivery fee to local `BridgeHub` + Some(( + XcmBridgeHubRouterFeeAssetId::get(), + bp_asset_hub_kusama::BridgeHubKusamaBaseFeeInDots::get(), + ).into()) ) ]; @@ -724,6 +755,7 @@ pub mod bridging { (BridgeHubKusamaWithBridgeHubPolkadotInstance::get(), GlobalConsensus(PolkadotNetwork::get())) ] ); + } impl Contains<(MultiLocation, Junction)> for UniversalAliases { @@ -735,11 +767,6 @@ pub mod bridging { pub type FilteredNetworkExportTable = parachains_common::xcm_config::FilteredNetworkExportTable; - /// Bridge router, which wraps and sends xcm to BridgeHub to be delivered to the different - /// GlobalConsensus - pub type BridgingXcmRouter = - UnpaidRemoteExporter; - /// Reserve locations filter for `xcm_executor::Config::IsReserve`. pub type IsTrustedBridgedReserveLocationForConcreteAsset = matching::IsTrustedBridgedReserveLocationForConcreteAsset< @@ -755,6 +782,22 @@ pub mod bridging { IsNotAllowedConcreteAssetBy, >; + impl Contains for ToPolkadotXcmRouter { + fn contains(call: &RuntimeCall) -> bool { + matches!( + call, + RuntimeCall::ToPolkadotXcmRouter( + pallet_xcm_bridge_hub_router::Call::report_bridge_status { .. } + ) + ) + } + } + + /// Barrier for `pallet_xcm_bridge_hub_router::Pallet` to receive congestion statuses from + /// sibling BridgeHub. + pub type AllowUnpaidStatusReportsFromSiblingBridgeHub = + AllowUnpaidTransactsFrom>; + /// Benchmarks helper for bridging configuration. #[cfg(feature = "runtime-benchmarks")] pub struct BridgingBenchmarksHelper; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/tests/tests.rs index 106a09d4e55c5..d62d7479f9df4 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/tests/tests.rs @@ -27,7 +27,8 @@ pub use asset_hub_kusama_runtime::{ }, AllPalletsWithoutSystem, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, Runtime, - RuntimeCall, RuntimeEvent, SessionKeys, System, TrustBackedAssetsInstance, XcmpQueue, + RuntimeCall, RuntimeEvent, SessionKeys, System, ToPolkadotXcmRouterInstance, + TrustBackedAssetsInstance, XcmpQueue, }; use asset_test_utils::{CollatorSessionKey, CollatorSessionKeys, ExtBuilder}; use codec::{Decode, Encode}; @@ -677,6 +678,7 @@ fn limited_reserve_transfer_assets_for_native_asset_over_bridge_works() { }), bridging_to_asset_hub_polkadot, WeightLimit::Unlimited, + Some(xcm_config::bridging::XcmBridgeHubRouterFeeAssetId::get()), ) } @@ -694,7 +696,7 @@ fn receive_reserve_asset_deposited_dot_from_asset_hub_polkadot_works() { ExistentialDeposit::get(), AccountId::from([73; 32]), AccountId::from(BLOCK_AUTHOR_ACCOUNT), - // receiving DOTs + // receiving DOTs (MultiLocation { parents: 2, interior: X1(GlobalConsensus(Polkadot)) }, 1000000000000, 1_000_000_000), bridging_to_asset_hub_polkadot, (X1(PalletInstance(53)), GlobalConsensus(Polkadot), X1(Parachain(1000))) @@ -784,3 +786,127 @@ fn xcm_reserve_transfer_filter_works() { } }) } + +#[test] +fn change_xcm_bridge_hub_router_byte_fee_by_governance_works() { + asset_test_utils::test_cases::change_storage_constant_by_governance_works::< + Runtime, + bridging::XcmBridgeHubRouterByteFee, + Balance, + >( + collator_session_keys(), + 1000, + Box::new(|call| RuntimeCall::System(call).encode()), + || { + ( + bridging::XcmBridgeHubRouterByteFee::key().to_vec(), + bridging::XcmBridgeHubRouterByteFee::get(), + ) + }, + |old_value| { + if let Some(new_value) = old_value.checked_add(1) { + new_value + } else { + old_value.checked_sub(1).unwrap() + } + }, + ) +} + +#[test] +fn test_report_bridge_status_call_compatibility() { + // if this test fails, make sure `bp_asset_hub_kusama` has valid encoding + assert_eq!( + RuntimeCall::ToPolkadotXcmRouter( + pallet_xcm_bridge_hub_router::Call::report_bridge_status { + bridge_id: Default::default(), + is_congested: true, + } + ) + .encode(), + bp_asset_hub_kusama::Call::ToPolkadotXcmRouter( + bp_asset_hub_kusama::XcmBridgeHubRouterCall::report_bridge_status { + bridge_id: Default::default(), + is_congested: true, + } + ) + .encode() + ) +} + +#[test] +fn report_bridge_status_from_xcm_bridge_router_works() { + asset_test_utils::test_cases_over_bridge::report_bridge_status_from_xcm_bridge_router_works::< + Runtime, + AllPalletsWithoutSystem, + XcmConfig, + ParachainSystem, + XcmpQueue, + LocationToAccountId, + ToPolkadotXcmRouterInstance, + >( + collator_session_keys(), + ExistentialDeposit::get(), + AccountId::from(ALICE), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(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, + WeightLimit::Unlimited, + Some(xcm_config::bridging::XcmBridgeHubRouterFeeAssetId::get()), + || { + sp_std::vec![Transact { + origin_kind: OriginKind::Xcm, + require_weight_at_most: + bp_asset_hub_kusama::XcmBridgeHubRouterTransactCallMaxWeight::get(), + call: bp_asset_hub_kusama::Call::ToPolkadotXcmRouter( + bp_asset_hub_kusama::XcmBridgeHubRouterCall::report_bridge_status { + bridge_id: Default::default(), + is_congested: true, + } + ) + .encode() + .into(), + }] + .into() + }, + || { + sp_std::vec![Transact { + origin_kind: OriginKind::Xcm, + require_weight_at_most: + bp_asset_hub_kusama::XcmBridgeHubRouterTransactCallMaxWeight::get(), + call: bp_asset_hub_kusama::Call::ToPolkadotXcmRouter( + bp_asset_hub_kusama::XcmBridgeHubRouterCall::report_bridge_status { + bridge_id: Default::default(), + is_congested: false, + } + ) + .encode() + .into(), + }] + .into() + }, + ) +} + +#[test] +fn check_sane_weight_report_bridge_status() { + use pallet_xcm_bridge_hub_router::WeightInfo; + let actual = >::WeightInfo::report_bridge_status(); + let max_weight = bp_asset_hub_kusama::XcmBridgeHubRouterTransactCallMaxWeight::get(); + assert!( + actual.all_lte(max_weight), + "max_weight: {:?} should be adjusted to actual {:?}", + max_weight, + actual + ); +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/Cargo.toml index dfc3d5416cc3c..59d937f673227 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/Cargo.toml @@ -66,7 +66,7 @@ cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-fea cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false, features = ["parameterized-consensus-hook",] } cumulus-pallet-session-benchmarking = { path = "../../../../pallets/session-benchmarking", default-features = false} cumulus-pallet-xcm = { path = "../../../../pallets/xcm", default-features = false } -cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } +cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false, features = ["bridging"] } cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } @@ -74,6 +74,10 @@ parachain-info = { path = "../../../pallets/parachain-info", default-features = parachains-common = { path = "../../../common", default-features = false } assets-common = { path = "../common", default-features = false } +# Bridges +pallet-xcm-bridge-hub-router = { path = "../../../../bridges/modules/xcm-bridge-hub-router", default-features = false } +bp-asset-hub-polkadot = { path = "../../../../bridges/primitives/chain-asset-hub-polkadot", default-features = false } + [dev-dependencies] hex-literal = "0.4.1" asset-test-utils = { path = "../test-utils" } @@ -104,6 +108,7 @@ runtime-benchmarks = [ "pallet-uniques/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", + "pallet-xcm-bridge-hub-router/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", @@ -135,6 +140,7 @@ try-runtime = [ "pallet-transaction-payment/try-runtime", "pallet-uniques/try-runtime", "pallet-utility/try-runtime", + "pallet-xcm-bridge-hub-router/try-runtime", "pallet-xcm/try-runtime", "parachain-info/try-runtime", "polkadot-runtime-common/try-runtime", @@ -142,6 +148,7 @@ try-runtime = [ ] std = [ "assets-common/std", + "bp-asset-hub-polkadot/std", "codec/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", @@ -176,6 +183,7 @@ std = [ "pallet-uniques/std", "pallet-utility/std", "pallet-xcm-benchmarks?/std", + "pallet-xcm-bridge-hub-router/std", "pallet-xcm/std", "parachain-info/std", "parachains-common/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/lib.rs index 2e63d630dd60d..cadd44c46c404 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/lib.rs @@ -729,6 +729,38 @@ impl pallet_nfts::Config for Runtime { type Helper = (); } +/// XCM router instance to BridgeHub with bridging capabilities for `Kusama` global consensus with +/// dynamic fees and back-pressure. +pub type ToKusamaXcmRouterInstance = pallet_assets::Instance1; +impl pallet_xcm_bridge_hub_router::Config for Runtime { + type WeightInfo = weights::pallet_xcm_bridge_hub_router::WeightInfo; + + type UniversalLocation = xcm_config::UniversalLocation; + type BridgedNetworkId = xcm_config::bridging::KusamaNetwork; + type Bridges = xcm_config::bridging::FilteredNetworkExportTable; + + #[cfg(not(feature = "runtime-benchmarks"))] + type BridgeHubOrigin = + EnsureXcm>; + #[cfg(feature = "runtime-benchmarks")] + type BridgeHubOrigin = EitherOfDiverse< + // for running benchmarks + EnsureRoot, + // for running tests with `--feature runtime-benchmarks` + EnsureXcm>, + >; + + type ToBridgeHubSender = XcmpQueue; + type WithBridgeHubChannel = + cumulus_pallet_xcmp_queue::bridging::InboundAndOutboundXcmpChannelCongestionStatusProvider< + xcm_config::bridging::BridgeHubPolkadotParaId, + Runtime, + >; + + type ByteFee = xcm_config::bridging::XcmBridgeHubRouterByteFee; + type FeeAsset = xcm_config::bridging::XcmBridgeHubRouterFeeAssetId; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime @@ -765,6 +797,9 @@ construct_runtime!( Multisig: pallet_multisig::{Pallet, Call, Storage, Event} = 41, Proxy: pallet_proxy::{Pallet, Call, Storage, Event} = 42, + // Bridge utilities. + ToKusamaXcmRouter: pallet_xcm_bridge_hub_router::::{Pallet, Storage, Call} = 43, + // The main stage. Assets: pallet_assets::::{Pallet, Call, Storage, Event} = 50, Uniques: pallet_uniques::{Pallet, Call, Storage, Event} = 51, @@ -828,6 +863,7 @@ mod benches { [pallet_timestamp, Timestamp] [pallet_collator_selection, CollatorSelection] [cumulus_pallet_xcmp_queue, XcmpQueue] + [pallet_xcm_bridge_hub_router, XcmBridgeHubRouterBench] // XCM [pallet_xcm, PolkadotXcm] // NOTE: Make sure you point to the individual modules below. @@ -1043,6 +1079,7 @@ impl_runtime_apis! { use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; + use pallet_xcm_bridge_hub_router::benchmarking::Pallet as XcmBridgeHubRouterBench; // This is defined once again in dispatch_benchmark, because list_benchmarks! // and add_benchmarks! are macros exported by define_benchmarks! macros and those types @@ -1085,6 +1122,25 @@ impl_runtime_apis! { use cumulus_pallet_session_benchmarking::Pallet as SessionBench; impl cumulus_pallet_session_benchmarking::Config for Runtime {} + use pallet_xcm_bridge_hub_router::benchmarking::{ + Pallet as XcmBridgeHubRouterBench, + Config as XcmBridgeHubRouterConfig, + }; + + impl XcmBridgeHubRouterConfig for Runtime { + fn make_congested() { + cumulus_pallet_xcmp_queue::bridging::suspend_channel_for_benchmarks::( + xcm_config::bridging::BridgeHubPolkadotParaId::get().into() + ); + } + fn ensure_bridged_target_destination() -> MultiLocation { + ParachainSystem::open_outbound_hrmp_channel_for_benchmarks( + xcm_config::bridging::BridgeHubPolkadotParaId::get().into() + ); + xcm_config::bridging::AssetHubKusama::get() + } + } + use xcm::latest::prelude::*; use xcm_config::{DotLocation, MaxAssetsIntoHolding}; use pallet_xcm_benchmarks::asset_instance_from; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/weights/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/weights/mod.rs index 3eb3b925e6518..18255317e233c 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/weights/mod.rs @@ -30,6 +30,7 @@ pub mod pallet_timestamp; pub mod pallet_uniques; pub mod pallet_utility; pub mod pallet_xcm; +pub mod pallet_xcm_bridge_hub_router; pub mod paritydb_weights; pub mod rocksdb_weights; pub mod xcm; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/weights/pallet_xcm_bridge_hub_router.rs b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/weights/pallet_xcm_bridge_hub_router.rs new file mode 100644 index 0000000000000..2fdc9747f81fc --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/weights/pallet_xcm_bridge_hub_router.rs @@ -0,0 +1,118 @@ +// 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_xcm_bridge_hub_router` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-08-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-aahe6cbd-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! 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=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_xcm_bridge_hub_router +// --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_xcm_bridge_hub_router`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm_bridge_hub_router::WeightInfo for WeightInfo { + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::InboundXcmpStatus` (r:1 w:0) + /// Proof: `XcmpQueue::InboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ToKusamaXcmRouter::Bridge` (r:1 w:1) + /// Proof: `ToKusamaXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) + fn on_initialize_when_non_congested() -> Weight { + // Proof Size summary in bytes: + // Measured: `193` + // Estimated: `1678` + // Minimum execution time: 9_963_000 picoseconds. + Weight::from_parts(10_410_000, 0) + .saturating_add(Weight::from_parts(0, 1678)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn on_initialize_when_congested() -> Weight { + // Proof Size summary in bytes: + // Measured: `111` + // Estimated: `1596` + // Minimum execution time: 3_322_000 picoseconds. + Weight::from_parts(3_513_000, 0) + .saturating_add(Weight::from_parts(0, 1596)) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `ToKusamaXcmRouter::Bridge` (r:1 w:1) + /// Proof: `ToKusamaXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) + fn report_bridge_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `117` + // Estimated: `1502` + // Minimum execution time: 11_296_000 picoseconds. + Weight::from_parts(11_778_000, 0) + .saturating_add(Weight::from_parts(0, 1502)) + .saturating_add(T::DbWeight::get().reads(1)) + .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 KEY `0x0973fe64c85043ba1c965cbc38eb63c7` (r:1 w:0) + /// Proof: UNKNOWN KEY `0x0973fe64c85043ba1c965cbc38eb63c7` (r:1 w:0) + /// Storage: `ToKusamaXcmRouter::Bridge` (r:1 w:1) + /// Proof: `ToKusamaXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) + /// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) + /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) + /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0) + /// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1) + /// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn send_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `355` + // Estimated: `3820` + // Minimum execution time: 50_336_000 picoseconds. + Weight::from_parts(51_994_000, 0) + .saturating_add(Weight::from_parts(0, 3820)) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs index 4b11eb6682c53..5479eb82d70e8 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs @@ -16,7 +16,7 @@ use super::{ AccountId, AllPalletsWithSystem, Assets, Authorship, Balance, Balances, ForeignAssets, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, - TrustBackedAssetsInstance, WeightToFee, XcmpQueue, + ToKusamaXcmRouter, TransactionByteFee, TrustBackedAssetsInstance, WeightToFee, XcmpQueue, }; use crate::ForeignAssetsInstance; use assets_common::matching::{ @@ -28,7 +28,10 @@ use frame_support::{ }; use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; -use parachains_common::{impls::ToStakingPot, xcm_config::AssetFeeAsExistentialDepositMultiplier}; +use parachains_common::{ + impls::ToStakingPot, + xcm_config::{AllowUnpaidTransactsFrom, AssetFeeAsExistentialDepositMultiplier}, +}; use polkadot_parachain_primitives::primitives::Sibling; use sp_runtime::traits::ConvertInto; use xcm::latest::prelude::*; @@ -209,6 +212,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().all(|(k, _)| k.eq(&bridging::XcmBridgeHubRouterByteFee::key())) => + return true, + _ => (), + }; + matches!( call, RuntimeCall::PolkadotXcm(pallet_xcm::Call::force_xcm_version { .. }) | @@ -351,7 +362,9 @@ impl Contains for SafeCallFilter { pallet_uniques::Call::set_accept_ownership { .. } | pallet_uniques::Call::set_collection_max_supply { .. } | pallet_uniques::Call::set_price { .. } | - pallet_uniques::Call::buy_item { .. } + pallet_uniques::Call::buy_item { .. }, + ) | RuntimeCall::ToKusamaXcmRouter( + pallet_xcm_bridge_hub_router::Call::report_bridge_status { .. } ) ) } @@ -379,6 +392,8 @@ pub type Barrier = TrailingSetTopicAsId< )>, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, + // Allows Transacts with `report_bridge_status` from sibling BridgeHub. + bridging::AllowUnpaidStatusReportsFromSiblingBridgeHub, ), UniversalLocation, ConstU32<8>, @@ -465,6 +480,7 @@ impl xcm_executor::Config for XcmConfig { type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); + // TODO:check-parameter: change and assert in tests when (https://github.com/paritytech/polkadot/pull/7005) merged type FeeManager = (); type MessageExporter = (); type UniversalAliases = bridging::UniversalAliases; @@ -487,7 +503,11 @@ type LocalXcmRouter = ( /// The means for routing XCM messages which are not for local execution into the right message /// queues. -pub type XcmRouter = WithUniqueTopic<(LocalXcmRouter, bridging::BridgingXcmRouter)>; +pub type XcmRouter = WithUniqueTopic<( + LocalXcmRouter, + // Router which wraps and sends XCM to BridgeHub to be delivered to the Kusama GlobalConsensus + ToKusamaXcmRouter, +)>; #[cfg(feature = "runtime-benchmarks")] parameter_types! { @@ -574,7 +594,6 @@ pub mod bridging { use assets_common::{matching, matching::*}; use parachains_common::xcm_config::LocationFilter; use sp_std::collections::btree_set::BTreeSet; - use xcm_builder::UnpaidRemoteExporter; parameter_types! { pub BridgeHubPolkadotParaId: u32 = 1002; @@ -584,7 +603,14 @@ pub mod bridging { pub AssetHubKusama: MultiLocation = MultiLocation::new(2, X2(GlobalConsensus(KusamaNetwork::get()), Parachain(1000))); pub KsmLocation: MultiLocation = MultiLocation::new(2, X1(GlobalConsensus(KusamaNetwork::get()))); + /// Router expects payment with this `AssetId`. + /// (`AssetId` has to be aligned with `BridgeTable`) + pub XcmBridgeHubRouterFeeAssetId: AssetId = DotLocation::get().into(); + /// Price per byte - can be adjusted via governance `set_storage` call. + pub storage XcmBridgeHubRouterByteFee: Balance = TransactionByteFee::get(); + /// Set up exporters configuration. + /// `Option` represents static "base fee" which is used for total delivery fee calculation. pub BridgeTable: sp_std::vec::Vec<(NetworkId, LocationFilter, MultiLocation, Option)> = sp_std::vec![ ( KusamaNetwork::get(), @@ -593,7 +619,11 @@ pub mod bridging { .add_equals(AssetHubKusama::get().interior.split_global().expect("invalid configuration for AssetHubKusama").1), // and nothing else BridgeHubPolkadot::get(), - None + // base delivery fee to local `BridgeHub` + Some(( + XcmBridgeHubRouterFeeAssetId::get(), + bp_asset_hub_polkadot::BridgeHubPolkadotBaseFeeInDots::get() + ).into()) ) ]; @@ -644,11 +674,6 @@ pub mod bridging { pub type FilteredNetworkExportTable = parachains_common::xcm_config::FilteredNetworkExportTable; - /// Bridge router, which wraps and sends xcm to BridgeHub to be delivered to the different - /// GlobalConsensus - pub type BridgingXcmRouter = - UnpaidRemoteExporter; - /// Reserve locations filter for `xcm_executor::Config::IsReserve`. pub type IsTrustedBridgedReserveLocationForConcreteAsset = matching::IsTrustedBridgedReserveLocationForConcreteAsset< @@ -664,6 +689,22 @@ pub mod bridging { IsNotAllowedConcreteAssetBy, >; + impl Contains for ToKusamaXcmRouter { + fn contains(call: &RuntimeCall) -> bool { + matches!( + call, + RuntimeCall::ToKusamaXcmRouter( + pallet_xcm_bridge_hub_router::Call::report_bridge_status { .. } + ) + ) + } + } + + /// Barrier for `pallet_xcm_bridge_hub_router::Pallet` to receive congestion statuses from + /// sibling BridgeHub. + pub type AllowUnpaidStatusReportsFromSiblingBridgeHub = + AllowUnpaidTransactsFrom>; + /// Benchmarks helper for bridging configuration. #[cfg(feature = "runtime-benchmarks")] pub struct BridgingBenchmarksHelper; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/tests/tests.rs index f76f11bac1768..7c9b00903ea9b 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/tests/tests.rs @@ -23,9 +23,10 @@ use asset_hub_polkadot_runtime::xcm_config::{ XcmConfig, }; pub use asset_hub_polkadot_runtime::{ - AllPalletsWithoutSystem, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, - ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, Runtime, - RuntimeCall, RuntimeEvent, SessionKeys, System, TrustBackedAssetsInstance, XcmpQueue, + xcm_config, AllPalletsWithoutSystem, AssetDeposit, Assets, Balances, ExistentialDeposit, + ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, + ParachainSystem, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, System, + ToKusamaXcmRouterInstance, TrustBackedAssetsInstance, XcmpQueue, }; use asset_test_utils::{CollatorSessionKey, CollatorSessionKeys, ExtBuilder}; use codec::{Decode, Encode}; @@ -699,6 +700,7 @@ fn limited_reserve_transfer_assets_for_native_asset_over_bridge_works() { }), bridging_to_asset_hub_kusama, WeightLimit::Unlimited, + Some(xcm_config::bridging::XcmBridgeHubRouterFeeAssetId::get()), ) } @@ -716,7 +718,7 @@ fn receive_reserve_asset_deposited_ksm_from_asset_hub_kusama_works() { ExistentialDeposit::get(), AccountId::from([73; 32]), AccountId::from(BLOCK_AUTHOR_ACCOUNT), - // receiving DOTs + // receiving KSMs (MultiLocation { parents: 2, interior: X1(GlobalConsensus(Kusama)) }, 1000000000000, 10_000_000_000), bridging_to_asset_hub_kusama, (X1(PalletInstance(53)), GlobalConsensus(Kusama), X1(Parachain(1000))) @@ -806,3 +808,125 @@ fn xcm_reserve_transfer_filter_works() { } }) } + +#[test] +fn change_xcm_bridge_hub_router_byte_fee_by_governance_works() { + asset_test_utils::test_cases::change_storage_constant_by_governance_works::< + Runtime, + bridging::XcmBridgeHubRouterByteFee, + Balance, + >( + collator_session_keys(), + 1000, + Box::new(|call| RuntimeCall::System(call).encode()), + || { + ( + bridging::XcmBridgeHubRouterByteFee::key().to_vec(), + bridging::XcmBridgeHubRouterByteFee::get(), + ) + }, + |old_value| { + if let Some(new_value) = old_value.checked_add(1) { + new_value + } else { + old_value.checked_sub(1).unwrap() + } + }, + ) +} + +#[test] +fn test_report_bridge_status_call_compatibility() { + // if this test fails, make sure `bp_asset_hub_polkadot` has valid encoding + assert_eq!( + RuntimeCall::ToKusamaXcmRouter(pallet_xcm_bridge_hub_router::Call::report_bridge_status { + bridge_id: Default::default(), + is_congested: true, + }) + .encode(), + bp_asset_hub_polkadot::Call::ToKusamaXcmRouter( + bp_asset_hub_polkadot::XcmBridgeHubRouterCall::report_bridge_status { + bridge_id: Default::default(), + is_congested: true, + } + ) + .encode() + ) +} + +#[test] +fn report_bridge_status_from_xcm_bridge_router_works() { + asset_test_utils::test_cases_over_bridge::report_bridge_status_from_xcm_bridge_router_works::< + Runtime, + AllPalletsWithoutSystem, + XcmConfig, + ParachainSystem, + XcmpQueue, + LocationToAccountId, + ToKusamaXcmRouterInstance, + >( + collator_session_keys(), + ExistentialDeposit::get(), + AccountId::from(ALICE), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(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, + WeightLimit::Unlimited, + Some(xcm_config::bridging::XcmBridgeHubRouterFeeAssetId::get()), + || { + sp_std::vec![Transact { + origin_kind: OriginKind::Xcm, + require_weight_at_most: + bp_asset_hub_polkadot::XcmBridgeHubRouterTransactCallMaxWeight::get(), + call: bp_asset_hub_polkadot::Call::ToKusamaXcmRouter( + bp_asset_hub_polkadot::XcmBridgeHubRouterCall::report_bridge_status { + bridge_id: Default::default(), + is_congested: true, + } + ) + .encode() + .into(), + }] + .into() + }, + || { + sp_std::vec![Transact { + origin_kind: OriginKind::Xcm, + require_weight_at_most: + bp_asset_hub_polkadot::XcmBridgeHubRouterTransactCallMaxWeight::get(), + call: bp_asset_hub_polkadot::Call::ToKusamaXcmRouter( + bp_asset_hub_polkadot::XcmBridgeHubRouterCall::report_bridge_status { + bridge_id: Default::default(), + is_congested: false, + } + ) + .encode() + .into(), + }] + .into() + }, + ) +} + +#[test] +fn check_sane_weight_report_bridge_status() { + use pallet_xcm_bridge_hub_router::WeightInfo; + let actual = >::WeightInfo::report_bridge_status(); + let max_weight = bp_asset_hub_polkadot::XcmBridgeHubRouterTransactCallMaxWeight::get(); + assert!( + actual.all_lte(max_weight), + "max_weight: {:?} should be adjusted to actual {:?}", + max_weight, + actual + ); +} diff --git a/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml b/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml index 6913144191029..1e0169782287a 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml @@ -41,6 +41,9 @@ xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkad pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false} polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false} +# Bridges +pallet-xcm-bridge-hub-router = { path = "../../../../bridges/modules/xcm-bridge-hub-router", default-features = false } + [dev-dependencies] hex-literal = "0.4.1" @@ -63,6 +66,7 @@ std = [ "pallet-balances/std", "pallet-collator-selection/std", "pallet-session/std", + "pallet-xcm-bridge-hub-router/std", "pallet-xcm/std", "parachain-info/std", "parachains-common/std", diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs index a815367587ab0..a0ec6bb4f7d12 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs @@ -58,6 +58,7 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works< >, ensure_configuration: fn() -> TestBridgingConfig, weight_limit: WeightLimit, + maybe_paid_export_message: Option, ) where Runtime: frame_system::Config + pallet_balances::Config @@ -174,6 +175,7 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works< )); // check alice account decreased by balance_to_transfer + // TODO:check-parameter: change and assert in tests when (https://github.com/paritytech/polkadot/pull/7005) merged assert_eq!( >::free_balance(&alice_account), alice_account_init_balance - balance_to_transfer.into() @@ -217,38 +219,54 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works< ); let mut xcm_sent: Xcm<()> = xcm_sent.try_into().expect("versioned xcm"); - // check sent XCM ExportMessage to bridge-hub - 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 - ); - assert_matches_pallet_xcm_reserve_transfer_assets_instructions(inner_xcm); - Ok(()) - }, - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("contains ExportMessage"); + // check sent XCM ExportMessage to BridgeHub + + // 1. check paid or unpaid + if let Some(expected_fee_asset_id) = maybe_paid_export_message { + xcm_sent + .0 + .matcher() + .match_next_inst(|instr| match instr { + WithdrawAsset(_) => Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains WithdrawAsset") + .match_next_inst(|instr| match instr { + BuyExecution { fees, .. } if fees.id.eq(&expected_fee_asset_id) => Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains BuyExecution") + } else { + xcm_sent + .0 + .matcher() + .match_next_inst(|instr| match instr { + // first instruction could be UnpaidExecution (because we could have + // explicit unpaid execution on BridgeHub) + UnpaidExecution { weight_limit, check_origin } + if weight_limit == &Unlimited && check_origin.is_none() => + Ok(()), + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains UnpaidExecution") + } + // 2. check ExportMessage + .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); + assert_matches_pallet_xcm_reserve_transfer_assets_instructions(inner_xcm); + Ok(()) + }, + _ => Err(ProcessMessageError::BadFormat), + }) + .expect("contains ExportMessage"); }) } @@ -502,3 +520,102 @@ fn assert_matches_pallet_xcm_reserve_transfer_assets_instructions( }) .expect("expected instruction DepositAsset"); } + +pub fn report_bridge_status_from_xcm_bridge_router_works< + Runtime, + AllPalletsWithoutSystem, + XcmConfig, + HrmpChannelOpener, + HrmpChannelSource, + LocationToAccountId, + XcmBridgeHubRouterInstance, +>( + collator_session_keys: CollatorSessionKeys, + existential_deposit: BalanceOf, + alice_account: AccountIdOf, + unwrap_pallet_xcm_event: Box) -> Option>>, + unwrap_xcmp_queue_event: Box< + dyn Fn(Vec) -> Option>, + >, + ensure_configuration: fn() -> TestBridgingConfig, + weight_limit: WeightLimit, + maybe_paid_export_message: Option, + congested_message: fn() -> Xcm, + uncongested_message: fn() -> Xcm, +) 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_xcm_bridge_hub_router::Config, + AllPalletsWithoutSystem: + OnInitialize> + OnFinalize>, + 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>, + ::AccountId: From, + HrmpChannelOpener: frame_support::inherent::ProvideInherent< + Call = cumulus_pallet_parachain_system::Call, + >, + HrmpChannelSource: XcmpMessageSource, + XcmBridgeHubRouterInstance: 'static, +{ + ExtBuilder::::default() + .with_collators(collator_session_keys.collators()) + .with_session_keys(collator_session_keys.session_keys()) + .with_tracing() + .build() + .execute_with(|| { + // check transfer works + limited_reserve_transfer_assets_for_native_asset_works::< + Runtime, + AllPalletsWithoutSystem, + XcmConfig, + HrmpChannelOpener, + HrmpChannelSource, + LocationToAccountId, + >( + collator_session_keys, + existential_deposit, + alice_account, + unwrap_pallet_xcm_event, + unwrap_xcmp_queue_event, + ensure_configuration, + weight_limit, + maybe_paid_export_message, + ); + + let report_brigde_status = |is_congested: bool| { + // prepare bridge config + let TestBridgingConfig { local_bridge_hub_location, .. } = ensure_configuration(); + + // Call received XCM execution + let xcm = if is_congested { congested_message() } else { uncongested_message() }; + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + + // execute xcm as XcmpQueue would do + let outcome = XcmExecutor::::execute_xcm( + local_bridge_hub_location, + xcm, + hash, + RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), + ); + assert_eq!(outcome.ensure_complete(), Ok(())); + assert_eq!(is_congested, pallet_xcm_bridge_hub_router::Pallet::::bridge().is_congested); + }; + + report_brigde_status(true); + report_brigde_status(false); + }) +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/README.md b/cumulus/parachains/runtimes/bridge-hubs/README.md index 56b6ad3ed6202..7600c52d35301 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/README.md +++ b/cumulus/parachains/runtimes/bridge-hubs/README.md @@ -266,7 +266,9 @@ This initialization does several things: - drips SA for AssetHubKusama on AssetHubPolkadot (and vice versa) which holds reserved assets on source chains ``` ./scripts/bridges_kusama_polkadot.sh init-asset-hub-kusama-local +./scripts/bridges_kusama_polkadot.sh init-bridge-hub-kusama-local ./scripts/bridges_kusama_polkadot.sh init-asset-hub-polkadot-local +./scripts/bridges_kusama_polkadot.sh init-bridge-hub-polkadot-local ``` ### 4. Send messages - transfer asset over bridge (DOTs/KSMs) diff --git a/cumulus/parachains/runtimes/test-utils/src/lib.rs b/cumulus/parachains/runtimes/test-utils/src/lib.rs index 3f13b78fb59ba..1e73de9f53d31 100644 --- a/cumulus/parachains/runtimes/test-utils/src/lib.rs +++ b/cumulus/parachains/runtimes/test-utils/src/lib.rs @@ -410,16 +410,20 @@ impl) -> Option>>, assert_outcome: fn(Outcome), ) { - let outcome = >::events() + assert_outcome(Self::get_pallet_xcm_event_outcome(unwrap_pallet_xcm_event)); + } + + pub fn get_pallet_xcm_event_outcome( + unwrap_pallet_xcm_event: &Box) -> Option>>, + ) -> Outcome { + >::events() .into_iter() .filter_map(|e| unwrap_pallet_xcm_event(e.event.encode())) .find_map(|e| match e { pallet_xcm::Event::Attempted { outcome } => Some(outcome), _ => None, }) - .expect("No `pallet_xcm::Event::Attempted(outcome)` event found!"); - - assert_outcome(outcome); + .expect("No `pallet_xcm::Event::Attempted(outcome)` event found!") } } diff --git a/cumulus/scripts/bridges_kusama_polkadot.sh b/cumulus/scripts/bridges_kusama_polkadot.sh index 43ca5356d2c02..a417ef49a9cae 100755 --- a/cumulus/scripts/bridges_kusama_polkadot.sh +++ b/cumulus/scripts/bridges_kusama_polkadot.sh @@ -36,6 +36,12 @@ ASSET_HUB_POLKADOT_ACCOUNT_ADDRESS_FOR_LOCAL="5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNe # &MultiLocation { parents: 2, interior: X2(GlobalConsensus(Kusama), Parachain(1000)) }).unwrap() # ).to_ss58check_with_version(0_u16.into()) # ); +# println!("ASSET_HUB_POLKADOT_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_POLKADOT=\"{}\"", +# frame_support::sp_runtime::AccountId32::new( +# SiblingParachainConvertsVia::::convert_location( +# &MultiLocation { parents: 1, interior: X1(Parachain(1000)) }).unwrap() +# ).to_ss58check_with_version(0_u16.into()) +# ); # # // SS58=2 # println!("GLOBAL_CONSENSUS_POLKADOT_SOVEREIGN_ACCOUNT=\"{}\"", @@ -50,11 +56,19 @@ ASSET_HUB_POLKADOT_ACCOUNT_ADDRESS_FOR_LOCAL="5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNe # &MultiLocation { parents: 2, interior: X2(GlobalConsensus(Polkadot), Parachain(1000)) }).unwrap() # ).to_ss58check_with_version(2_u16.into()) # ); +# println!("ASSET_HUB_KUSAMA_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_KUSAMA=\"{}\"", +# frame_support::sp_runtime::AccountId32::new( +# SiblingParachainConvertsVia::::convert_location( +# &MultiLocation { parents: 1, interior: X1(Parachain(1000)) }).unwrap() +# ).to_ss58check_with_version(2_u16.into()) +# ); # } GLOBAL_CONSENSUS_KUSAMA_SOVEREIGN_ACCOUNT="14zcUAhP5XypiFQWA3b1AnGKrhZqR4XWUo4deWkwuN5y983G" GLOBAL_CONSENSUS_KUSAMA_ASSET_HUB_KUSAMA_1000_SOVEREIGN_ACCOUNT="12GvRkNCmXFuaaziTJ2ZKAfa7MArKfLT2HYvLjQuepP3JuHf" +ASSET_HUB_POLKADOT_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_POLKADOT="13cKp89SgdtqUngo2WiEijPrQWdHFhzYZLf2TJePKRvExk7o" GLOBAL_CONSENSUS_POLKADOT_SOVEREIGN_ACCOUNT="FxqimVubBRPqJ8kTwb3wL7G4q645hEkBEnXPyttLsTrFc5Q" GLOBAL_CONSENSUS_POLKADOT_ASSET_HUB_POLKADOT_1000_SOVEREIGN_ACCOUNT="FwGjEp7GXJXT9NjH8r4sqdyd8XZVogbxSs3iFakx4wFwJ5Y" +ASSET_HUB_KUSAMA_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_KUSAMA="FBeL7EFTDeHnuViqaUHUXvhhUusN5FawDmHgfvzF97DXFr3" function init_ksm_dot() { ensure_relayer @@ -136,6 +150,14 @@ case "$1" in "$GLOBAL_CONSENSUS_POLKADOT_ASSET_HUB_POLKADOT_1000_SOVEREIGN_ACCOUNT" \ $((1000000000 + 50000000000 * 20)) ;; + init-bridge-hub-kusama-local) + # SA of sibling asset hub pays for the execution + transfer_balance \ + "ws://127.0.0.1:8943" \ + "//Alice" \ + "$ASSET_HUB_KUSAMA_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_KUSAMA" \ + $((1000000000 + 50000000000 * 20)) + ;; init-asset-hub-polkadot-local) # create foreign assets for native Polkadot token (governance call on Kusama) force_create_foreign_asset \ @@ -154,6 +176,14 @@ case "$1" in "$GLOBAL_CONSENSUS_KUSAMA_ASSET_HUB_KUSAMA_1000_SOVEREIGN_ACCOUNT" \ $((1000000000 + 50000000000 * 20)) ;; + init-bridge-hub-polkadot-local) + # SA of sibling asset hub pays for the execution + transfer_balance \ + "ws://127.0.0.1:8945" \ + "//Alice" \ + "$ASSET_HUB_POLKADOT_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_POLKADOT" \ + $((1000000000 + 50000000000 * 20)) + ;; reserve-transfer-assets-from-asset-hub-kusama-local) ensure_polkadot_js_api # send KSMs to Alice account on AHP @@ -187,7 +217,9 @@ case "$1" in Local (zombienet) run: - run-relay - init-asset-hub-kusama-local + - init-bridge-hub-kusama-local - init-asset-hub-polkadot-local + - init-bridge-hub-polkadot-local - reserve-transfer-assets-from-asset-hub-kusama-local - reserve-transfer-assets-from-asset-hub-polkadot-local"; exit 1