diff --git a/Cargo.lock b/Cargo.lock index e908fb691d2..93c54c9a956 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1271,6 +1271,26 @@ dependencies = [ "thiserror", ] +[[package]] +name = "bp-asset-hub-kusama" +version = "0.1.0" +dependencies = [ + "bp-xcm-bridge-hub-router", + "frame-support", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "bp-asset-hub-polkadot" +version = "0.1.0" +dependencies = [ + "bp-xcm-bridge-hub-router", + "frame-support", + "parity-scale-codec", + "scale-info", +] + [[package]] name = "bp-bridge-hub-cumulus" version = "0.1.0" @@ -1521,6 +1541,7 @@ dependencies = [ name = "bridge-hub-kusama-runtime" version = "0.1.0" dependencies = [ + "bp-asset-hub-kusama", "bp-bridge-hub-kusama", "bp-bridge-hub-polkadot", "bp-header-chain", @@ -1601,6 +1622,7 @@ dependencies = [ name = "bridge-hub-polkadot-runtime" version = "0.1.0" dependencies = [ + "bp-asset-hub-polkadot", "bp-bridge-hub-kusama", "bp-bridge-hub-polkadot", "bp-header-chain", @@ -3248,6 +3270,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", @@ -9008,6 +9031,25 @@ dependencies = [ "xcm-executor", ] +[[package]] +name = "pallet-xcm-bridge-hub-router" +version = "0.1.0" +dependencies = [ + "bp-xcm-bridge-hub-router", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", + "xcm-builder", +] + [[package]] name = "parachain-info" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 4ae4ea191ae..95841e9444d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "bridges/modules/messages", "bridges/modules/parachains", "bridges/modules/relayers", + "bridges/modules/xcm-bridge-hub-router", "client/cli", "client/collator", "client/consensus/aura", diff --git a/pallets/xcmp-queue/Cargo.toml b/pallets/xcmp-queue/Cargo.toml index 842e3ce9af8..994009aa00f 100644 --- a/pallets/xcmp-queue/Cargo.toml +++ b/pallets/xcmp-queue/Cargo.toml @@ -28,6 +28,9 @@ cumulus-primitives-core = { path = "../../primitives/core", default-features = f # Optional import for benchmarking frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "master" } +# Bridges +bp-xcm-bridge-hub-router = { path = "../../bridges/primitives/xcm-bridge-hub-router", default-features = false, optional = true } + [dev-dependencies] # Substrate @@ -55,6 +58,7 @@ std = [ "sp-std/std", "xcm-executor/std", "xcm/std", + "bp-xcm-bridge-hub-router/std", ] runtime-benchmarks = [ @@ -64,3 +68,7 @@ runtime-benchmarks = [ "xcm-builder/runtime-benchmarks", ] try-runtime = ["frame-support/try-runtime"] + +bridging = [ + "bp-xcm-bridge-hub-router", +] diff --git a/pallets/xcmp-queue/src/bridging.rs b/pallets/xcmp-queue/src/bridging.rs new file mode 100644 index 00000000000..e39357d88c1 --- /dev/null +++ b/pallets/xcmp-queue/src/bridging.rs @@ -0,0 +1,111 @@ +// 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 + } +} diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index 960af9b5b77..e5e4ce28e15 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/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/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 03b98dad740..e97e69c022e 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -269,7 +269,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/parachains/runtimes/bridge-hubs/bridge-hub-kusama/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/Cargo.toml index de6ef9aa10d..fd408c4106a 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/Cargo.toml @@ -63,7 +63,7 @@ cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-fea cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false } cumulus-pallet-session-benchmarking = {path = "../../../../pallets/session-benchmarking", default-features = false, version = "3.0.0"} 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-timestamp = { path = "../../../../primitives/timestamp", default-features = false } cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } @@ -72,6 +72,7 @@ parachain-info = { path = "../../../../parachains/pallets/parachain-info", defau parachains-common = { path = "../../../../parachains/common", default-features = false } # Bridges +bp-asset-hub-kusama = { path = "../../../../bridges/primitives/chain-asset-hub-kusama", default-features = false } bp-bridge-hub-kusama = { path = "../../../../bridges/primitives/chain-bridge-hub-kusama", default-features = false } bp-bridge-hub-polkadot = { path = "../../../../bridges/primitives/chain-bridge-hub-polkadot", default-features = false } bp-header-chain = { path = "../../../../bridges/primitives/header-chain", default-features = false } @@ -99,6 +100,7 @@ default = [ "std", ] std = [ + "bp-asset-hub-kusama/std", "bp-bridge-hub-kusama/std", "bp-bridge-hub-polkadot/std", "bp-header-chain/std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/bridge_hub_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/bridge_hub_config.rs index 47456be4011..9707937d89e 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/bridge_hub_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/bridge_hub_config.rs @@ -33,6 +33,7 @@ use bridge_runtime_common::{ RefundableParachain, }, }; +use codec::Encode; use frame_support::{parameter_types, traits::PalletInfoAccess, RuntimeDebug}; use xcm::{latest::prelude::*, prelude::NetworkId}; use xcm_builder::{BridgeBlobDispatcher, HaulBlobExporter}; @@ -54,6 +55,28 @@ parameter_types! { ParentThen(X1(Parachain(AssetHubKusamaParaId::get().into()))).into(), ASSET_HUB_KUSAMA_TO_ASSET_HUB_POLKADOT_LANE_ID, ); + + pub CongestedMessage: Xcm<()> = 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(); + + pub UncongestedMessage: Xcm<()> = 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(); } /// Proof of messages, coming from BridgeHubPolkadot. @@ -80,10 +103,13 @@ impl XcmBlobHauler for ToBridgeHubPolkadotXcmBlobHauler { type SenderAndLane = FromAssetHubKusamaToAssetHubPolkadotRoute; type ToSourceChainSender = crate::XcmRouter; - type CongestedMessage = (); - type UncongestedMessage = (); + type CongestedMessage = CongestedMessage; + type UncongestedMessage = UncongestedMessage; } +/// On messages delivered callback. +pub type OnMessagesDelivered = XcmBlobHaulerAdapter; + /// Messaging Bridge configuration for ThisChain -> BridgeHubPolkadot pub struct WithBridgeHubPolkadotMessageBridge; impl MessageBridge for WithBridgeHubPolkadotMessageBridge { diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs index d61a61a41ed..b3fe8a57bc5 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs @@ -485,9 +485,15 @@ impl pallet_bridge_messages::Config for R DeliveryRewardInBalance, >; type SourceHeaderChain = SourceHeaderChainAdapter; - type MessageDispatch = - XcmBlobMessageDispatch, Self::WeightInfo, ()>; - type OnMessagesDelivered = (); + type MessageDispatch = XcmBlobMessageDispatch< + OnThisChainBlobDispatcher, + Self::WeightInfo, + cumulus_pallet_xcmp_queue::bridging::OutboundXcmpChannelCongestionStatusProvider< + bridge_hub_config::AssetHubKusamaParaId, + Runtime, + >, + >; + type OnMessagesDelivered = bridge_hub_config::OnMessagesDelivered; } /// Allows collect and claim rewards for relayers diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index fa6b56649c1..24ee0b1c871 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -112,12 +112,6 @@ match_types! { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(_) } }; - // TODO:check-parameter - (https://github.com/paritytech/parity-bridges-common/issues/2084) - // remove this and extend `AllowExplicitUnpaidExecutionFrom` with "or SystemParachains" once merged https://github.com/paritytech/polkadot/pull/7005 - pub type SystemParachains: impl Contains = { - // Statemine - MultiLocation { parents: 1, interior: X1(Parachain(1000)) } - }; } /// A call filter for the XCM Transact instruction. This is a temporary measure until we properly @@ -191,9 +185,8 @@ pub type Barrier = TrailingSetTopicAsId< // If the message is one that immediately attemps to pay for execution, then // allow it. AllowTopLevelPaidExecutionFrom, - // Parent, its pluralities (i.e. governance bodies) and system parachains get - // free execution. - AllowExplicitUnpaidExecutionFrom<(ParentOrParentsPlurality, SystemParachains)>, + // Parent and its pluralities (i.e. governance bodies) get free execution. + AllowExplicitUnpaidExecutionFrom<(ParentOrParentsPlurality,)>, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, ), diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/tests/tests.rs index b64f0b58f8a..cc67e19bd3f 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/tests/tests.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/tests/tests.rs @@ -18,7 +18,7 @@ use bp_polkadot_core::Signature; pub use bridge_hub_kusama_runtime::{ bridge_hub_config, constants::fee::WeightToFee, - xcm_config::{RelayNetwork, XcmConfig}, + xcm_config::{KsmRelayLocation, RelayNetwork, XcmConfig}, Balances, BridgeGrandpaPolkadotInstance, BridgeRejectObsoleteHeadersAndMessages, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, WithBridgeHubPolkadotMessagesInstance, @@ -183,7 +183,10 @@ fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() { } }), || ExportMessage { network: Polkadot, destination: X1(Parachain(1234)), xcm: Xcm(vec![]) }, - bridge_hub_config::ASSET_HUB_KUSAMA_TO_ASSET_HUB_POLKADOT_LANE_ID + bridge_hub_config::ASSET_HUB_KUSAMA_TO_ASSET_HUB_POLKADOT_LANE_ID, + Some((KsmRelayLocation::get(), ExistentialDeposit::get()).into()), + // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer` + Some((KsmRelayLocation::get(), bp_asset_hub_kusama::BridgeHubKusamaBaseFeeInDots::get()).into()), ) } @@ -259,3 +262,21 @@ pub fn complex_relay_extrinsic_works() { construct_and_apply_extrinsic, ); } + +#[test] +pub fn can_calculate_weight_for_paid_export_message_with_reserve_transfer() { + let estimated = bridge_hub_test_utils::test_cases::can_calculate_weight_for_paid_export_message_with_reserve_transfer::< + Runtime, + XcmConfig, + WeightToFee, + >(); + + // check if estimated value is sane + let max_expected = bp_asset_hub_kusama::BridgeHubKusamaBaseFeeInDots::get(); + assert!( + estimated <= max_expected, + "calculated: {:?}, max_expected: {:?}, please adjust `bp_asset_hub_kusama::BridgeHubKusamaBaseFeeInDots` value", + estimated, + max_expected + ); +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/Cargo.toml index a606d71c0d3..0361152faf7 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/Cargo.toml @@ -63,7 +63,7 @@ cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-fea cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false } cumulus-pallet-session-benchmarking = {path = "../../../../pallets/session-benchmarking", default-features = false, version = "3.0.0"} 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-timestamp = { path = "../../../../primitives/timestamp", default-features = false } cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } @@ -72,6 +72,7 @@ parachain-info = { path = "../../../../parachains/pallets/parachain-info", defau parachains-common = { path = "../../../../parachains/common", default-features = false } # Bridges +bp-asset-hub-polkadot = { path = "../../../../bridges/primitives/chain-asset-hub-polkadot", default-features = false } bp-bridge-hub-kusama = { path = "../../../../bridges/primitives/chain-bridge-hub-kusama", default-features = false } bp-bridge-hub-polkadot = { path = "../../../../bridges/primitives/chain-bridge-hub-polkadot", default-features = false } bp-header-chain = { path = "../../../../bridges/primitives/header-chain", default-features = false } @@ -99,6 +100,7 @@ default = [ "std", ] std = [ + "bp-asset-hub-polkadot/std", "bp-bridge-hub-kusama/std", "bp-bridge-hub-polkadot/std", "bp-header-chain/std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/bridge_hub_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/bridge_hub_config.rs index 76afabbb84d..37c975fcafa 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/bridge_hub_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/bridge_hub_config.rs @@ -33,6 +33,7 @@ use bridge_runtime_common::{ RefundableParachain, }, }; +use codec::Encode; use frame_support::{parameter_types, traits::PalletInfoAccess, RuntimeDebug}; use xcm::{latest::prelude::*, prelude::NetworkId}; use xcm_builder::{BridgeBlobDispatcher, HaulBlobExporter}; @@ -54,6 +55,28 @@ parameter_types! { ParentThen(X1(Parachain(AssetHubPolkadotParaId::get().into()))).into(), ASSET_HUB_POLKADOT_TO_ASSET_HUB_KUSAMA_LANE_ID, ); + + pub CongestedMessage: Xcm<()> = 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(); + + pub UncongestedMessage: Xcm<()> = 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(); } /// Proof of messages, coming from BridgeHubKusama. @@ -80,10 +103,13 @@ impl XcmBlobHauler for ToBridgeHubKusamaXcmBlobHauler { type SenderAndLane = FromAssetHubPolkadotToAssetHubKusamaRoute; type ToSourceChainSender = crate::XcmRouter; - type CongestedMessage = (); - type UncongestedMessage = (); + type CongestedMessage = CongestedMessage; + type UncongestedMessage = UncongestedMessage; } +/// On messages delivered callback. +pub type OnMessagesDelivered = XcmBlobHaulerAdapter; + /// Messaging Bridge configuration for ThisChain -> BridgeHubKusama pub struct WithBridgeHubKusamaMessageBridge; impl MessageBridge for WithBridgeHubKusamaMessageBridge { diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/lib.rs index b54c19cd459..9d616657780 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/lib.rs @@ -486,9 +486,15 @@ impl pallet_bridge_messages::Config for Run DeliveryRewardInBalance, >; type SourceHeaderChain = SourceHeaderChainAdapter; - type MessageDispatch = - XcmBlobMessageDispatch, Self::WeightInfo, ()>; - type OnMessagesDelivered = (); + type MessageDispatch = XcmBlobMessageDispatch< + OnThisChainBlobDispatcher, + Self::WeightInfo, + cumulus_pallet_xcmp_queue::bridging::OutboundXcmpChannelCongestionStatusProvider< + bridge_hub_config::AssetHubPolkadotParaId, + Runtime, + >, + >; + type OnMessagesDelivered = bridge_hub_config::OnMessagesDelivered; } /// Allows collect and claim rewards for relayers diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs index 9f5cbba783b..364318d4c13 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs @@ -115,14 +115,6 @@ match_types! { pub type FellowsPlurality: impl Contains = { MultiLocation { parents: 1, interior: X2(Parachain(1001), Plurality { id: BodyId::Technical, ..}) } }; - // TODO:check-parameter - (https://github.com/paritytech/parity-bridges-common/issues/2084) - // remove this and extend `AllowExplicitUnpaidExecutionFrom` with "or SystemParachains" once merged https://github.com/paritytech/polkadot/pull/7005 - pub type SystemParachains: impl Contains = { - // Statemint - MultiLocation { parents: 1, interior: X1(Parachain(1000)) } | - // Collectives - MultiLocation { parents: 1, interior: X1(Parachain(1001)) } - }; } /// A call filter for the XCM Transact instruction. This is a temporary measure until we properly @@ -196,13 +188,9 @@ pub type Barrier = TrailingSetTopicAsId< // If the message is one that immediately attemps to pay for execution, then // allow it. AllowTopLevelPaidExecutionFrom, - // Parent, its pluralities (i.e. governance bodies), the Fellows plurality - // and system parachains get free execution. - AllowExplicitUnpaidExecutionFrom<( - ParentOrParentsPlurality, - FellowsPlurality, - SystemParachains, - )>, + // Parent, its pluralities (i.e. governance bodies) and the Fellows plurality + // get free execution. + AllowExplicitUnpaidExecutionFrom<(ParentOrParentsPlurality, FellowsPlurality)>, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, ), diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/tests/tests.rs index 49861c3cff9..ededd8d8c18 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/tests/tests.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/tests/tests.rs @@ -18,7 +18,7 @@ use bp_polkadot_core::Signature; pub use bridge_hub_polkadot_runtime::{ bridge_hub_config, constants::fee::WeightToFee, - xcm_config::{RelayNetwork, XcmConfig}, + xcm_config::{DotRelayLocation, RelayNetwork, XcmConfig}, Balances, BridgeGrandpaKusamaInstance, BridgeRejectObsoleteHeadersAndMessages, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, WithBridgeHubKusamaMessagesInstance, @@ -183,7 +183,10 @@ fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() { } }), || ExportMessage { network: Kusama, destination: X1(Parachain(1234)), xcm: Xcm(vec![]) }, - bridge_hub_config::ASSET_HUB_POLKADOT_TO_ASSET_HUB_KUSAMA_LANE_ID + bridge_hub_config::ASSET_HUB_POLKADOT_TO_ASSET_HUB_KUSAMA_LANE_ID, + Some((DotRelayLocation::get(), ExistentialDeposit::get()).into()), + // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer` + Some((DotRelayLocation::get(), bp_asset_hub_polkadot::BridgeHubPolkadotBaseFeeInDots::get()).into()), ) } @@ -259,3 +262,21 @@ pub fn complex_relay_extrinsic_works() { construct_and_apply_extrinsic, ); } + +#[test] +pub fn can_calculate_weight_for_paid_export_message_with_reserve_transfer() { + let estimated = bridge_hub_test_utils::test_cases::can_calculate_weight_for_paid_export_message_with_reserve_transfer::< + Runtime, + XcmConfig, + WeightToFee, + >(); + + // check if estimated value is sane + let max_expected = bp_asset_hub_polkadot::BridgeHubPolkadotBaseFeeInDots::get(); + assert!( + estimated <= max_expected, + "calculated: {:?}, max_expected: {:?}, please adjust `bp_asset_hub_polkadot::BridgeHubPolkadotBaseFeeInDots` value", + estimated, + max_expected + ); +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index 890d39e049c..e704216abfd 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -191,7 +191,9 @@ mod bridge_hub_rococo_tests { } }), || ExportMessage { network: Wococo, destination: X1(Parachain(1234)), xcm: Xcm(vec![]) }, - bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO + bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO, + None, + None, ) } @@ -366,7 +368,9 @@ mod bridge_hub_wococo_tests { } }), || ExportMessage { network: Rococo, destination: X1(Parachain(4321)), xcm: Xcm(vec![]) }, - bridge_hub_wococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO + bridge_hub_wococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO, + None, + None, ) } diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs index 02843501967..8f6b0dd0d05 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs +++ b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs @@ -45,10 +45,16 @@ use parachains_runtimes_test_utils::{ }; use sp_core::H256; use sp_keyring::AccountKeyring::*; -use sp_runtime::{traits::Header as HeaderT, AccountId32}; +use sp_runtime::{ + traits::{Header as HeaderT, Zero}, + AccountId32, +}; use xcm::latest::prelude::*; use xcm_builder::DispatchBlobError; -use xcm_executor::XcmExecutor; +use xcm_executor::{ + traits::{TransactAsset, WeightBounds}, + XcmExecutor, +}; // Re-export test_case from assets pub use asset_test_utils::include_teleports_for_native_asset_works; @@ -132,6 +138,8 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works< >, export_message_instruction: fn() -> Instruction, expected_lane_id: LaneId, + existential_deposit: Option, + maybe_paid_export_message: Option, ) where Runtime: frame_system::Config + pallet_balances::Config @@ -165,10 +173,35 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works< ); // prepare `ExportMessage` - let xcm = Xcm(vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - export_message_instruction(), - ]); + let xcm = if let Some(fee) = maybe_paid_export_message { + // deposit ED to origin (if needed) + if let Some(ed) = existential_deposit { + XcmConfig::AssetTransactor::deposit_asset( + &ed, + &sibling_parachain_location, + &XcmContext::with_message_id([0; 32]), + ) + .expect("deposited ed"); + } + // deposit fee to origin + XcmConfig::AssetTransactor::deposit_asset( + &fee, + &sibling_parachain_location, + &XcmContext::with_message_id([0; 32]), + ) + .expect("deposited fee"); + + Xcm(vec![ + WithdrawAsset(MultiAssets::from(vec![fee.clone()])), + BuyExecution { fees: fee, weight_limit: Unlimited }, + export_message_instruction(), + ]) + } else { + Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + export_message_instruction(), + ]) + }; // execute XCM let hash = xcm.using_encoded(sp_io::hashing::blake2_256); @@ -758,6 +791,112 @@ pub fn complex_relay_extrinsic_works() -> u128 +where + Runtime: frame_system::Config + pallet_balances::Config, + XcmConfig: xcm_executor::Config, + WeightToFee: frame_support::weights::WeightToFee>, + ::Balance: From + Into, +{ + // data here are not relevant for weighing + let mut xcm = Xcm(vec![ + WithdrawAsset(MultiAssets::from(vec![MultiAsset { + id: Concrete(MultiLocation { parents: 1, interior: Here }), + fun: Fungible(34333299), + }])), + BuyExecution { + fees: MultiAsset { + id: Concrete(MultiLocation { parents: 1, interior: Here }), + fun: Fungible(34333299), + }, + weight_limit: Unlimited, + }, + ExportMessage { + network: Polkadot, + destination: X1(Parachain(1000)), + xcm: Xcm(vec![ + ReserveAssetDeposited(MultiAssets::from(vec![MultiAsset { + id: Concrete(MultiLocation { + parents: 2, + interior: X1(GlobalConsensus(Kusama)), + }), + fun: Fungible(1000000000000), + }])), + ClearOrigin, + BuyExecution { + fees: MultiAsset { + id: Concrete(MultiLocation { + parents: 2, + interior: X1(GlobalConsensus(Kusama)), + }), + fun: Fungible(1000000000000), + }, + weight_limit: Unlimited, + }, + DepositAsset { + assets: Wild(AllCounted(1)), + beneficiary: MultiLocation { + parents: 0, + interior: X1(xcm::latest::prelude::AccountId32 { + network: None, + id: [ + 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, + 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, + 109, 162, 125, + ], + }), + }, + }, + SetTopic([ + 116, 82, 194, 132, 171, 114, 217, 165, 23, 37, 161, 177, 165, 179, 247, 114, + 137, 101, 147, 70, 28, 157, 168, 32, 154, 63, 74, 228, 152, 180, 5, 63, + ]), + ]), + }, + RefundSurplus, + DepositAsset { + assets: Wild(All), + beneficiary: MultiLocation { parents: 1, interior: X1(Parachain(1000)) }, + }, + SetTopic([ + 36, 224, 250, 165, 82, 195, 67, 110, 160, 170, 140, 87, 217, 62, 201, 164, 42, 98, 219, + 157, 124, 105, 248, 25, 131, 218, 199, 36, 109, 173, 100, 122, + ]), + ]); + + // get weight + let weight = XcmConfig::Weigher::weight(&mut xcm); + assert_ok!(weight); + let weight = weight.unwrap(); + // check if sane + let max_expected = Runtime::BlockWeights::get().max_block / 10; + assert!( + weight.all_lte(max_expected), + "calculated weight: {:?}, max_expected: {:?}", + weight, + max_expected + ); + + // check fee, should not be 0 + let estimated_fee = WeightToFee::weight_to_fee(&weight); + assert!(estimated_fee > BalanceOf::::zero()); + + frame_support::sp_tracing::try_init_simple(); + log::error!( + target: "bridges::estimate", + "Estimate fee: {:?} for `ExportMessage` for runtime: {:?}", + estimated_fee, + Runtime::Version::get(), + ); + + estimated_fee.into() +} + pub mod test_data { use super::*; use bp_header_chain::justification::GrandpaJustification; diff --git a/scripts/bridges_kusama_polkadot.sh b/scripts/bridges_kusama_polkadot.sh index 43ca5356d2c..a417ef49a9c 100755 --- a/scripts/bridges_kusama_polkadot.sh +++ b/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