Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 718d493

Browse files
committed
[dynfees] Implemented XcmChannelStatusProvider for XcmpQueue
1 parent e5ac886 commit 718d493

File tree

4 files changed

+137
-0
lines changed

4 files changed

+137
-0
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pallets/xcmp-queue/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ cumulus-primitives-core = { path = "../../primitives/core", default-features = f
2828
# Optional import for benchmarking
2929
frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "master" }
3030

31+
# Bridges
32+
bp-xcm-bridge-hub-router = { path = "../../bridges/primitives/xcm-bridge-hub-router", default-features = false, optional = true }
33+
3134
[dev-dependencies]
3235

3336
# Substrate
@@ -55,6 +58,7 @@ std = [
5558
"sp-std/std",
5659
"xcm-executor/std",
5760
"xcm/std",
61+
"bp-xcm-bridge-hub-router/std",
5862
]
5963

6064
runtime-benchmarks = [
@@ -64,3 +68,7 @@ runtime-benchmarks = [
6468
"xcm-builder/runtime-benchmarks",
6569
]
6670
try-runtime = ["frame-support/try-runtime"]
71+
72+
bridging = [
73+
"bp-xcm-bridge-hub-router",
74+
]

pallets/xcmp-queue/src/bridging.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright (C) Parity Technologies (UK) Ltd.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
use crate::pallet;
17+
use cumulus_primitives_core::ParaId;
18+
use frame_support::pallet_prelude::Get;
19+
20+
/// Adapter implementation for `bp_xcm_bridge_hub_router::XcmChannelStatusProvider` which checks
21+
/// both `OutboundXcmpStatus` and `InboundXcmpStatus` for defined `ParaId` if any of those is suspended.
22+
pub struct InboundAndOutboundXcmpChannelCongestionStatusProvider<SiblingBridgeHubParaId, Runtime>(
23+
sp_std::marker::PhantomData<(SiblingBridgeHubParaId, Runtime)>,
24+
);
25+
impl<SiblingBridgeHubParaId: Get<ParaId>, Runtime: crate::Config>
26+
bp_xcm_bridge_hub_router::XcmChannelStatusProvider
27+
for InboundAndOutboundXcmpChannelCongestionStatusProvider<SiblingBridgeHubParaId, Runtime>
28+
{
29+
fn is_congested() -> bool {
30+
// if the outbound channel with recipient is suspended, it means that one of further
31+
// bridge queues (e.g. bridge queue between two bridge hubs) is overloaded, so we shall
32+
// take larger fee for our outbound messages
33+
let sibling_bridge_hub_id: ParaId = SiblingBridgeHubParaId::get();
34+
let outbound_channels = pallet::OutboundXcmpStatus::<Runtime>::get();
35+
let outbound_channel =
36+
outbound_channels.iter().find(|c| c.recipient == sibling_bridge_hub_id);
37+
let is_outbound_channel_suspended =
38+
outbound_channel.map(|c| c.is_suspended()).unwrap_or(false);
39+
if is_outbound_channel_suspended {
40+
return true
41+
}
42+
43+
// if the inbound channel with recipient is suspended, it means that we are unable to receive
44+
// congestion reports from the bridge hub. So we assume the bridge pipeline is congested too
45+
let inbound_channels = pallet::InboundXcmpStatus::<Runtime>::get();
46+
let inbound_channel =
47+
inbound_channels.iter().find(|c| c.sender == sibling_bridge_hub_id);
48+
let is_inbound_channel_suspended =
49+
inbound_channel.map(|c| c.is_suspended()).unwrap_or(false);
50+
if is_inbound_channel_suspended {
51+
return true
52+
}
53+
54+
// TODO: https://github.com/paritytech/cumulus/pull/2342 - once this PR is merged, we may
55+
// remove the following code
56+
//
57+
// if the outbound channel has at least `N` pages enqueued, let's assume it is congested.
58+
// Normally, the chain with a few opened HRMP channels, will "send" pages at every block.
59+
// Having `N` pages means that for last `N` blocks we either have not sent any messages,
60+
// or have sent signals.
61+
const MAX_OUTBOUND_PAGES_BEFORE_CONGESTION: u16 = 4;
62+
let is_outbound_channel_congested = outbound_channel.map(|c| c.queued_pages()).unwrap_or(0);
63+
is_outbound_channel_congested > MAX_OUTBOUND_PAGES_BEFORE_CONGESTION
64+
}
65+
}
66+
67+
/// Adapter implementation for `bp_xcm_bridge_hub_router::XcmChannelStatusProvider` which checks
68+
/// only `OutboundXcmpStatus` for defined `SiblingParaId` if is suspended.
69+
pub struct OutboundXcmpChannelCongestionStatusProvider<SiblingBridgeHubParaId, Runtime>(
70+
sp_std::marker::PhantomData<(SiblingBridgeHubParaId, Runtime)>,
71+
);
72+
impl<SiblingParaId: Get<ParaId>, Runtime: crate::Config>
73+
bp_xcm_bridge_hub_router::XcmChannelStatusProvider
74+
for OutboundXcmpChannelCongestionStatusProvider<SiblingParaId, Runtime>
75+
{
76+
fn is_congested() -> bool {
77+
// let's find the channel with the sibling parachain
78+
let sibling_para_id: cumulus_primitives_core::ParaId = SiblingParaId::get();
79+
let outbound_channels = pallet::OutboundXcmpStatus::<Runtime>::get();
80+
let channel_with_sibling_parachain =
81+
outbound_channels.iter().find(|c| c.recipient == sibling_para_id);
82+
83+
// no channel => it is empty, so not congested
84+
let channel_with_sibling_parachain = match channel_with_sibling_parachain {
85+
Some(channel_with_sibling_parachain) => channel_with_sibling_parachain,
86+
None => return false,
87+
};
88+
89+
// suspended channel => it is congested
90+
if channel_with_sibling_parachain.is_suspended() {
91+
return true
92+
}
93+
94+
// TODO: the following restriction is arguable, we may live without that, assuming that there
95+
// can't be more than some `N` messages queued at the bridge queue (at the source BH) AND before
96+
// accepting next (or next-after-next) delivery transaction, we'll receive the suspension signal
97+
// from the target parachain and stop accepting delivery transactions
98+
99+
// it takes some time for target parachain to suspend inbound channel with the target BH and during that
100+
// we will keep accepting new message delivery transactions. Let's also reject new deliveries if
101+
// there are too many "pages" (concatenated XCM messages) in the target BH -> target parachain queue.
102+
const MAX_QUEUED_PAGES_BEFORE_DEACTIVATION: u16 = 4;
103+
if channel_with_sibling_parachain.queued_pages() > MAX_QUEUED_PAGES_BEFORE_DEACTIVATION {
104+
return true
105+
}
106+
107+
true
108+
}
109+
}

pallets/xcmp-queue/src/lib.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ mod tests;
3535

3636
#[cfg(feature = "runtime-benchmarks")]
3737
mod benchmarking;
38+
#[cfg(feature = "bridging")]
39+
pub mod bridging;
3840
pub mod weights;
3941
pub use weights::WeightInfo;
4042

@@ -398,6 +400,13 @@ pub struct InboundChannelDetails {
398400
message_metadata: Vec<(RelayBlockNumber, XcmpMessageFormat)>,
399401
}
400402

403+
impl InboundChannelDetails {
404+
#[cfg(feature = "bridging")]
405+
pub(crate) fn is_suspended(&self) -> bool {
406+
self.state == InboundState::Suspended
407+
}
408+
}
409+
401410
/// Struct containing detailed information about the outbound channel.
402411
#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
403412
pub struct OutboundChannelDetails {
@@ -433,6 +442,16 @@ impl OutboundChannelDetails {
433442
self.state = OutboundState::Suspended;
434443
self
435444
}
445+
446+
#[cfg(feature = "bridging")]
447+
pub(crate) fn is_suspended(&self) -> bool {
448+
self.state == OutboundState::Suspended
449+
}
450+
451+
#[cfg(feature = "bridging")]
452+
pub(crate) fn queued_pages(&self) -> u16 {
453+
self.last_index.saturating_sub(self.first_index)
454+
}
436455
}
437456

438457
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]

0 commit comments

Comments
 (0)