diff --git a/Cargo.lock b/Cargo.lock index 69eabeb04e20..26716453e9cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2147,6 +2147,7 @@ version = "0.2.0" dependencies = [ "bp-messages", "bp-runtime", + "bp-xcm-bridge-support", "frame-support", "parity-scale-codec", "scale-info", @@ -2161,10 +2162,17 @@ dependencies = [ name = "bp-xcm-bridge-hub-router" version = "0.6.0" dependencies = [ + "bp-xcm-bridge-support", "parity-scale-codec", "scale-info", "sp-core 28.0.0", "sp-runtime 31.0.1", +] + +[[package]] +name = "bp-xcm-bridge-support" +version = "0.6.0" +dependencies = [ "staging-xcm", ] @@ -4437,7 +4445,7 @@ name = "cumulus-pallet-xcmp-queue" version = "0.7.0" dependencies = [ "bounded-collections", - "bp-xcm-bridge-hub-router", + "bp-xcm-bridge-hub", "cumulus-pallet-parachain-system", "cumulus-primitives-core", "frame-benchmarking", diff --git a/Cargo.toml b/Cargo.toml index b7c9c0cdcbf1..8f908d5d5702 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ members = [ "bridges/primitives/test-utils", "bridges/primitives/xcm-bridge-hub", "bridges/primitives/xcm-bridge-hub-router", + "bridges/primitives/xcm-bridge-support", "bridges/relays/client-substrate", "bridges/relays/equivocation", "bridges/relays/finality", @@ -649,6 +650,7 @@ bp-test-utils = { path = "bridges/primitives/test-utils", default-features = fal bp-westend = { path = "bridges/chains/chain-westend", default-features = false } bp-xcm-bridge-hub = { path = "bridges/primitives/xcm-bridge-hub", default-features = false } bp-xcm-bridge-hub-router = { path = "bridges/primitives/xcm-bridge-hub-router", default-features = false } +bp-xcm-bridge-support = { path = "bridges/primitives/xcm-bridge-support", default-features = false } bridge-hub-common = { path = "cumulus/parachains/runtimes/bridge-hubs/common", default-features = false } bridge-hub-rococo-emulated-chain = { path = "cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo" } bridge-hub-rococo-runtime = { path = "cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo", default-features = false } diff --git a/bridges/modules/xcm-bridge-hub/src/dispatcher.rs b/bridges/modules/xcm-bridge-hub/src/dispatcher.rs index dd855c7069aa..94485b70c25c 100644 --- a/bridges/modules/xcm-bridge-hub/src/dispatcher.rs +++ b/bridges/modules/xcm-bridge-hub/src/dispatcher.rs @@ -25,7 +25,7 @@ use crate::{Config, Pallet, LOG_TARGET}; use bp_messages::target_chain::{DispatchMessage, MessageDispatch}; use bp_runtime::messages::MessageDispatchResult; -use bp_xcm_bridge_hub::{LocalXcmChannelManager, XcmAsPlainPayload}; +use bp_xcm_bridge_hub::{XcmAsPlainPayload, XcmChannelStatusProvider}; use codec::{Decode, Encode}; use frame_support::{weights::Weight, CloneNoBound, EqNoBound, PartialEqNoBound}; use pallet_bridge_messages::{Config as BridgeMessagesConfig, WeightInfoExt}; diff --git a/bridges/modules/xcm-bridge-hub/src/mock.rs b/bridges/modules/xcm-bridge-hub/src/mock.rs index 6511b9fc5b04..24adc9cd9f74 100644 --- a/bridges/modules/xcm-bridge-hub/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub/src/mock.rs @@ -415,10 +415,6 @@ impl TestLocalXcmChannelManager { impl LocalXcmChannelManager for TestLocalXcmChannelManager { type Error = (); - fn is_congested(_with: &Location) -> bool { - frame_support::storage::unhashed::get_or_default(b"TestLocalXcmChannelManager.Congested") - } - fn suspend_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> { frame_support::storage::unhashed::put(b"TestLocalXcmChannelManager.Suspended", &true); Ok(()) @@ -430,9 +426,9 @@ impl LocalXcmChannelManager for TestLocalXcmChannelManager { } } -impl pallet_xcm_bridge_hub_router::XcmChannelStatusProvider for TestLocalXcmChannelManager { - fn is_congested(with: &Location) -> bool { - ::is_congested(with) +impl bp_xcm_bridge_hub::XcmChannelStatusProvider for TestLocalXcmChannelManager { + fn is_congested(_with: &Location) -> bool { + frame_support::storage::unhashed::get_or_default(b"TestLocalXcmChannelManager.Congested") } } diff --git a/bridges/primitives/xcm-bridge-hub-router/Cargo.toml b/bridges/primitives/xcm-bridge-hub-router/Cargo.toml index ba0c51152bd2..3901aa0549be 100644 --- a/bridges/primitives/xcm-bridge-hub-router/Cargo.toml +++ b/bridges/primitives/xcm-bridge-hub-router/Cargo.toml @@ -14,13 +14,13 @@ workspace = true codec = { features = ["bit-vec", "derive"], workspace = true } scale-info = { features = ["bit-vec", "derive"], workspace = true } +# Bridge Dependencies +bp-xcm-bridge-support = { workspace = true } + # Substrate Dependencies sp-runtime = { workspace = true } sp-core = { workspace = true } -# Polkadot Dependencies -xcm = { workspace = true } - [features] default = ["std"] std = [ @@ -28,5 +28,4 @@ std = [ "scale-info/std", "sp-core/std", "sp-runtime/std", - "xcm/std", ] diff --git a/bridges/primitives/xcm-bridge-hub-router/src/lib.rs b/bridges/primitives/xcm-bridge-hub-router/src/lib.rs index 89123b51ef2f..568bdb301a67 100644 --- a/bridges/primitives/xcm-bridge-hub-router/src/lib.rs +++ b/bridges/primitives/xcm-bridge-hub-router/src/lib.rs @@ -18,30 +18,15 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub use bp_xcm_bridge_support::XcmChannelStatusProvider; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_core::H256; use sp_runtime::{FixedU128, RuntimeDebug}; -use xcm::latest::prelude::Location; /// Minimal delivery fee factor. pub const MINIMAL_DELIVERY_FEE_FACTOR: FixedU128 = FixedU128::from_u32(1); -/// XCM channel status provider that may report whether it is congested or not. -/// -/// By channel we mean the physical channel that is used to deliver messages of one -/// of the bridge queues. -pub trait XcmChannelStatusProvider { - /// Returns true if the channel is currently congested. - fn is_congested(with: &Location) -> bool; -} - -impl XcmChannelStatusProvider for () { - fn is_congested(_with: &Location) -> bool { - false - } -} - /// Current status of the bridge. #[derive(Clone, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, RuntimeDebug)] pub struct BridgeState { diff --git a/bridges/primitives/xcm-bridge-hub/Cargo.toml b/bridges/primitives/xcm-bridge-hub/Cargo.toml index 79201a8756f9..0ba2074a8dde 100644 --- a/bridges/primitives/xcm-bridge-hub/Cargo.toml +++ b/bridges/primitives/xcm-bridge-hub/Cargo.toml @@ -18,6 +18,7 @@ serde = { features = ["alloc", "derive"], workspace = true } # Bridge Dependencies bp-messages = { workspace = true } bp-runtime = { workspace = true } +bp-xcm-bridge-support = { workspace = true } # Substrate Dependencies sp-std = { workspace = true } diff --git a/bridges/primitives/xcm-bridge-hub/src/lib.rs b/bridges/primitives/xcm-bridge-hub/src/lib.rs index 061e7a275063..9c2bc82eee0a 100644 --- a/bridges/primitives/xcm-bridge-hub/src/lib.rs +++ b/bridges/primitives/xcm-bridge-hub/src/lib.rs @@ -21,6 +21,7 @@ use bp_messages::LaneIdType; use bp_runtime::{AccountIdOf, BalanceOf, Chain}; +pub use bp_xcm_bridge_support::XcmChannelStatusProvider; pub use call_info::XcmBridgeHubCall; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ @@ -96,16 +97,10 @@ impl core::fmt::Debug for BridgeId { } /// Local XCM channel manager. -pub trait LocalXcmChannelManager { +pub trait LocalXcmChannelManager: bp_xcm_bridge_support::XcmChannelStatusProvider { /// Error that may be returned when suspending/resuming the bridge. type Error: sp_std::fmt::Debug; - /// Returns true if the channel with given location is currently congested. - /// - /// The `with` is guaranteed to be in the same consensus. However, it may point to something - /// below the chain level - like the contract or pallet instance, for example. - fn is_congested(with: &Location) -> bool; - /// Suspend the bridge, opened by given origin. /// /// The `local_origin` is guaranteed to be in the same consensus. However, it may point to @@ -122,10 +117,6 @@ pub trait LocalXcmChannelManager { impl LocalXcmChannelManager for () { type Error = (); - fn is_congested(_with: &Location) -> bool { - false - } - fn suspend_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> { Ok(()) } diff --git a/bridges/primitives/xcm-bridge-support/Cargo.toml b/bridges/primitives/xcm-bridge-support/Cargo.toml new file mode 100644 index 000000000000..ef6467e988aa --- /dev/null +++ b/bridges/primitives/xcm-bridge-support/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "bp-xcm-bridge-support" +description = "Primitives for the xcm-like bridge." +version = "0.6.0" +authors.workspace = true +edition.workspace = true +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +# Polkadot Dependencies +xcm = { workspace = true } + +[features] +default = ["std"] +std = [ + "xcm/std", +] diff --git a/bridges/primitives/xcm-bridge-support/src/lib.rs b/bridges/primitives/xcm-bridge-support/src/lib.rs new file mode 100644 index 000000000000..c6b3915f33b6 --- /dev/null +++ b/bridges/primitives/xcm-bridge-support/src/lib.rs @@ -0,0 +1,39 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives for the xcm-like bridge. + +#![cfg_attr(not(feature = "std"), no_std)] + +use xcm::latest::prelude::Location; + +/// XCM channel status provider that may report whether it is congested or not. +/// +/// By channel, we mean the physical channel that is used to deliver messages to/of one +/// of the bridge queues. +pub trait XcmChannelStatusProvider { + /// Returns true if the channel with given location is currently congested. + /// + /// The `with` is guaranteed to be in the same consensus. However, it may point to something + /// below the chain level - like the contract or pallet instance, for example. + fn is_congested(with: &Location) -> bool; +} + +impl XcmChannelStatusProvider for () { + fn is_congested(_with: &Location) -> bool { + false + } +} diff --git a/cumulus/pallets/xcmp-queue/Cargo.toml b/cumulus/pallets/xcmp-queue/Cargo.toml index 9c7470eda6da..cb33b8232ab2 100644 --- a/cumulus/pallets/xcmp-queue/Cargo.toml +++ b/cumulus/pallets/xcmp-queue/Cargo.toml @@ -37,7 +37,7 @@ frame-benchmarking = { optional = true, workspace = true } bounded-collections = { workspace = true } # Bridges -bp-xcm-bridge-hub-router = { optional = true, workspace = true } +bp-xcm-bridge-hub = { optional = true, workspace = true } [dev-dependencies] @@ -53,7 +53,7 @@ cumulus-pallet-parachain-system = { workspace = true, default-features = true } default = ["std"] std = [ "bounded-collections/std", - "bp-xcm-bridge-hub-router?/std", + "bp-xcm-bridge-hub?/std", "codec/std", "cumulus-primitives-core/std", "frame-benchmarking?/std", @@ -96,4 +96,4 @@ try-runtime = [ "polkadot-runtime-parachains/try-runtime", "sp-runtime/try-runtime", ] -bridging = ["bp-xcm-bridge-hub-router"] +bridging = ["bp-xcm-bridge-hub"] diff --git a/cumulus/pallets/xcmp-queue/src/bridging.rs b/cumulus/pallets/xcmp-queue/src/bridging.rs index 8ed11505a27a..ae4d1706ecda 100644 --- a/cumulus/pallets/xcmp-queue/src/bridging.rs +++ b/cumulus/pallets/xcmp-queue/src/bridging.rs @@ -13,15 +13,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{pallet, OutboundState}; +use crate::{pallet, ChannelSignal, OutboundState, LOG_TARGET}; use cumulus_primitives_core::ParaId; use xcm::latest::prelude::*; -/// Adapter implementation for `bp_xcm_bridge_hub_router::XcmChannelStatusProvider` which checks +/// Adapter implementation for `XcmChannelStatusProvider` which checks /// both `OutboundXcmpStatus` and `InboundXcmpStatus` for defined `Location` if any of those is /// suspended. pub struct InAndOutXcmpChannelStatusProvider(core::marker::PhantomData); -impl bp_xcm_bridge_hub_router::XcmChannelStatusProvider +impl bp_xcm_bridge_hub::XcmChannelStatusProvider for InAndOutXcmpChannelStatusProvider { fn is_congested(with: &Location) -> bool { @@ -45,10 +45,64 @@ impl bp_xcm_bridge_hub_router::XcmChannelStatusProvider } } -/// Adapter implementation for `bp_xcm_bridge_hub_router::XcmChannelStatusProvider` which checks +impl bp_xcm_bridge_hub::LocalXcmChannelManager + for InAndOutXcmpChannelStatusProvider +{ + type Error = (); + fn suspend_bridge(with: &Location, _: bp_xcm_bridge_hub::BridgeId) -> Result<(), Self::Error> { + // handle congestion only for a sibling parachain locations. + let sibling_para_id: ParaId = match with.unpack() { + (_, [Parachain(para_id)]) => (*para_id).into(), + loc @ _ => + return { + log::trace!( + target: LOG_TARGET, + "Unhandled location with: {loc:?}" + ); + Ok(()) + }, + }; + + // report congestion = true + pallet::Pallet::::send_signal(sibling_para_id, ChannelSignal::ReportCongestion(true)) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Failed to send_signal(ReportCongestion(true)) to the sibling_para_id: `{sibling_para_id:?}` with error: {e:?}" + ); + () + }) + } + fn resume_bridge(with: &Location, _: bp_xcm_bridge_hub::BridgeId) -> Result<(), Self::Error> { + // handle congestion only for a sibling parachain locations. + let sibling_para_id: ParaId = match with.unpack() { + (_, [Parachain(para_id)]) => (*para_id).into(), + loc @ _ => + return { + log::trace!( + target: LOG_TARGET, + "Unhandled location with: {loc:?}" + ); + Ok(()) + }, + }; + + // report congestion = false + pallet::Pallet::::send_signal(sibling_para_id, ChannelSignal::ReportCongestion(false)) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Failed to send_signal(ReportCongestion(false)) to the sibling_para_id: `{sibling_para_id:?}` with error: {e:?}" + ); + () + }) + } +} + +/// Adapter implementation for `XcmChannelStatusProvider` which checks /// only `OutboundXcmpStatus` for defined `SiblingParaId` if is suspended. pub struct OutXcmpChannelStatusProvider(core::marker::PhantomData); -impl bp_xcm_bridge_hub_router::XcmChannelStatusProvider +impl bp_xcm_bridge_hub::XcmChannelStatusProvider for OutXcmpChannelStatusProvider { fn is_congested(with: &Location) -> bool { diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs index 6bb7395f6553..63b031d49839 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -384,6 +384,8 @@ pub struct OutboundChannelDetails { first_index: u16, /// The index of the last outbound message. last_index: u16, + /// Flag indicating that `recipient` reports congestion. + congestion_reported: bool, } impl OutboundChannelDetails { @@ -394,6 +396,7 @@ impl OutboundChannelDetails { signals_exist: false, first_index: 0, last_index: 0, + congestion_reported: false, } } @@ -406,6 +409,11 @@ impl OutboundChannelDetails { self.state = OutboundState::Suspended; self } + + pub fn with_congestion_reported(mut self) -> OutboundChannelDetails { + self.congestion_reported = true; + self + } } #[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] @@ -454,6 +462,7 @@ impl QueueConfigData { pub enum ChannelSignal { Suspend, Resume, + ReportCongestion(bool), } impl Pallet { @@ -520,6 +529,7 @@ impl Pallet { .expect("can't be empty; a new element was just pushed; qed") }; let have_active = channel_details.last_index > channel_details.first_index; + let is_congested = channel_details.congestion_reported; // Try to append fragment to the last page, if there is enough space. // We return the size of the last page inside of the option, to not calculate it again. let appended_to_last_page = have_active @@ -578,6 +588,12 @@ impl Pallet { let message_size_factor = FixedU128::from((encoded_fragment.len() / 1024) as u128) .saturating_mul(delivery_fee_constants::MESSAGE_SIZE_FEE_BASE); Self::increase_fee_factor(recipient, message_size_factor); + } else if is_congested { + // TODO: not sure about this is a good place + // if congested, then increase + let message_size_factor = FixedU128::from((encoded_fragment.len() / 1024) as u128) + .saturating_mul(delivery_fee_constants::MESSAGE_SIZE_FEE_BASE); + Self::increase_fee_factor(recipient, message_size_factor); } Ok(number_of_pages) @@ -638,6 +654,55 @@ impl Pallet { }); } + fn report_congestion(target: ParaId, is_congested: bool) { + log::trace!(target: LOG_TARGET, "Target `{target:?}` reports congestion is_congested: {is_congested:?}"); + + // check actual state + let is_already_congested = >::get() + .iter() + .find(|c| c.recipient == target) + .map(|c| c.congestion_reported) + .unwrap_or(false); + + // handle reported state + if is_congested { + Self::increase_fee_factor(target, delivery_fee_constants::MESSAGE_SIZE_FEE_BASE); + + // store `congestion_reported = true` + if !is_already_congested { + >::mutate(|s| { + if let Some(details) = s.iter_mut().find(|item| item.recipient == target) { + details.congestion_reported = true; + } else { + if s.try_push( + OutboundChannelDetails::new(target).with_congestion_reported(), + ) + .is_err() + { + defensive!("Cannot report congestion for a channel; too many outbound channels"); + } + } + }); + } + } else if is_already_congested { + // if reports not congested and was congested before, just decrease and clean up state + Self::decrease_fee_factor(target); + + // store `congestion_reported = false` + >::mutate(|s| { + if let Some(details) = s.iter_mut().find(|item| item.recipient == target) { + details.congestion_reported = false; + } else { + if s.try_push(OutboundChannelDetails::new(target)).is_err() { + defensive!( + "Cannot report congestion for a channel; too many outbound channels" + ); + } + } + }); + } + } + fn enqueue_xcmp_message( sender: ParaId, xcm: BoundedVec>, @@ -789,6 +854,8 @@ impl XcmpMessageHandler for Pallet { match ChannelSignal::decode(&mut data) { Ok(ChannelSignal::Suspend) => Self::suspend_channel(sender), Ok(ChannelSignal::Resume) => Self::resume_channel(sender), + Ok(ChannelSignal::ReportCongestion(is_congested)) => + Self::report_congestion(sender, is_congested), Err(_) => { defensive!("Undecodable channel signal - dropping"); break @@ -836,6 +903,7 @@ impl XcmpMessageSource for Pallet { mut signals_exist, mut first_index, mut last_index, + congestion_reported, } = *status; let (max_size_now, max_size_ever) = match T::ChannelInfo::get_channel_status(para_id) { @@ -910,12 +978,19 @@ impl XcmpMessageSource for Pallet { MAX_POSSIBLE_ALLOCATION // We use this as a fallback in case the messaging state is not present }, }; - let threshold = max_total_size.saturating_div(delivery_fee_constants::THRESHOLD_FACTOR); - let remaining_total_size: usize = (first_index..last_index) - .map(|index| OutboundXcmpMessages::::decode_len(para_id, index).unwrap()) - .sum(); - if remaining_total_size <= threshold as usize { - Self::decrease_fee_factor(para_id); + if congestion_reported { + // TODO: not sure about this is a good place + // if congested, then increase + Self::increase_fee_factor(para_id, delivery_fee_constants::MESSAGE_SIZE_FEE_BASE); + } else { + let threshold = + max_total_size.saturating_div(delivery_fee_constants::THRESHOLD_FACTOR); + let remaining_total_size: usize = (first_index..last_index) + .map(|index| OutboundXcmpMessages::::decode_len(para_id, index).unwrap()) + .sum(); + if remaining_total_size <= threshold as usize { + Self::decrease_fee_factor(para_id); + } } *status = OutboundChannelDetails { @@ -924,6 +999,7 @@ impl XcmpMessageSource for Pallet { signals_exist, first_index, last_index, + congestion_reported, }; } debug_assert!(!statuses.iter().any(|s| s.signals_exist), "Signals should be handled"); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs index 8fe045723107..4693152b48ae 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs @@ -155,8 +155,8 @@ impl pallet_xcm_bridge_hub::Config for Runtime type AllowWithoutBridgeDeposit = RelayOrOtherSystemParachains; - // TODO:(bridges-v2) - add `LocalXcmChannelManager` impl - https://github.com/paritytech/parity-bridges-common/issues/3047 - type LocalXcmChannelManager = (); + type LocalXcmChannelManager = + cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider; type BlobDispatcher = FromWestendMessageBlobDispatcher; } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs index e45654bc62bd..6f72659600a3 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs @@ -184,8 +184,8 @@ impl pallet_xcm_bridge_hub::Config for Runtime { type AllowWithoutBridgeDeposit = RelayOrOtherSystemParachains; - // TODO:(bridges-v2) - add `LocalXcmChannelManager` impl - https://github.com/paritytech/parity-bridges-common/issues/3047 - type LocalXcmChannelManager = (); + type LocalXcmChannelManager = + cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider; type BlobDispatcher = FromRococoMessageBlobDispatcher; }