use crate::{Bridges, Config, DispatchChannelStatusProvider, LOG_TARGET};
use bp_xcm_bridge::{BridgeId, LocalXcmChannelManager, Receiver};
use parity_scale_codec::{Decode, Encode};
use sp_runtime::traits::Convert;
use sp_std::{marker::PhantomData, vec::Vec};
use xcm::latest::{send_xcm, Location, SendXcm, Xcm};
use xcm_builder::{DispatchBlob, DispatchBlobError};
#[derive(Debug, Decode, Encode)]
pub struct CongestionLimits {
pub outbound_lane_congested_threshold: bp_messages::MessageNonce,
pub outbound_lane_uncongested_threshold: bp_messages::MessageNonce,
pub outbound_lane_stop_threshold: bp_messages::MessageNonce,
}
impl CongestionLimits {
pub fn is_valid(&self) -> bool {
self.outbound_lane_uncongested_threshold < self.outbound_lane_congested_threshold
&& self.outbound_lane_stop_threshold > self.outbound_lane_congested_threshold
}
}
impl Default for CongestionLimits {
fn default() -> Self {
Self {
outbound_lane_congested_threshold: 8_192,
outbound_lane_uncongested_threshold: 1_024,
outbound_lane_stop_threshold: 12_288,
}
}
}
pub struct HereOrLocalConsensusXcmChannelManager<
Bridge,
HereXcmChannelManager,
LocalConsensusXcmChannelManager,
>(
PhantomData<(
Bridge,
HereXcmChannelManager,
LocalConsensusXcmChannelManager,
)>,
);
impl<
Bridge: Encode + sp_std::fmt::Debug + Copy,
HereXcmChannelManager: LocalXcmChannelManager<Bridge>,
LocalConsensusXcmChannelManager: LocalXcmChannelManager<Bridge>,
> LocalXcmChannelManager<Bridge>
for HereOrLocalConsensusXcmChannelManager<
Bridge,
HereXcmChannelManager,
LocalConsensusXcmChannelManager,
>
{
type Error = ();
fn suspend_bridge(local_origin: &Location, bridge: Bridge) -> Result<(), Self::Error> {
if local_origin.eq(&Location::here()) {
HereXcmChannelManager::suspend_bridge(local_origin, bridge).map_err(|e| {
log::error!(
target: LOG_TARGET,
"HereXcmChannelManager::suspend_bridge error: {e:?} for local_origin: {:?} and bridge: {:?}",
local_origin,
bridge,
);
()
})
} else {
LocalConsensusXcmChannelManager::suspend_bridge(local_origin, bridge).map_err(|e| {
log::error!(
target: LOG_TARGET,
"LocalConsensusXcmChannelManager::suspend_bridge error: {e:?} for local_origin: {:?} and bridge: {:?}",
local_origin,
bridge,
);
()
})
}
}
fn resume_bridge(local_origin: &Location, bridge: Bridge) -> Result<(), Self::Error> {
if local_origin.eq(&Location::here()) {
HereXcmChannelManager::resume_bridge(local_origin, bridge).map_err(|e| {
log::error!(
target: LOG_TARGET,
"HereXcmChannelManager::resume_bridge error: {e:?} for local_origin: {:?} and bridge: {:?}",
local_origin,
bridge,
);
()
})
} else {
LocalConsensusXcmChannelManager::resume_bridge(local_origin, bridge).map_err(|e| {
log::error!(
target: LOG_TARGET,
"LocalConsensusXcmChannelManager::resume_bridge error: {e:?} for local_origin: {:?} and bridge: {:?}",
local_origin,
bridge,
);
()
})
}
}
}
pub struct UpdateBridgeStatusXcmChannelManager<T, I, XcmProvider, XcmSender>(
PhantomData<(T, I, XcmProvider, XcmSender)>,
);
impl<T: Config<I>, I: 'static, XcmProvider: Convert<Vec<u8>, Xcm<()>>, XcmSender: SendXcm>
UpdateBridgeStatusXcmChannelManager<T, I, XcmProvider, XcmSender>
{
fn update_bridge_status(
local_origin: &Location,
bridge_id: BridgeId,
is_congested: bool,
) -> Result<(), ()> {
let bridge = Bridges::<T, I>::get(&bridge_id).ok_or(())?;
let Some(Receiver {
pallet_index,
call_index,
}) = bridge.maybe_notify
else {
return Ok(());
};
let remote_runtime_call = (pallet_index, call_index, bridge_id, is_congested);
let xcm = XcmProvider::convert(remote_runtime_call.encode());
log::trace!(
target: LOG_TARGET,
"UpdateBridgeStatusXcmChannelManager is going to send status with is_congested: {:?} to the local_origin: {:?} and bridge: {:?} as xcm: {:?}",
is_congested,
local_origin,
bridge,
xcm,
);
send_xcm::<XcmSender>(local_origin.clone(), xcm)
.map(|result| {
log::warn!(
target: LOG_TARGET,
"UpdateBridgeStatusXcmChannelManager successfully sent status with is_congested: {:?} to the local_origin: {:?} and bridge: {:?} with result: {:?}",
is_congested,
local_origin,
bridge,
result,
);
()
})
.map_err(|e| {
log::error!(
target: LOG_TARGET,
"UpdateBridgeStatusXcmChannelManager failed to send status with is_congested: {:?} to the local_origin: {:?} and bridge: {:?} with error: {:?}",
is_congested,
local_origin,
bridge,
e,
);
()
})
}
}
impl<T: Config<I>, I: 'static, XcmProvider: Convert<Vec<u8>, Xcm<()>>, XcmSender: SendXcm>
LocalXcmChannelManager<BridgeId>
for UpdateBridgeStatusXcmChannelManager<T, I, XcmProvider, XcmSender>
{
type Error = ();
fn suspend_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error> {
Self::update_bridge_status(local_origin, bridge, true)
}
fn resume_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error> {
Self::update_bridge_status(local_origin, bridge, false)
}
}
pub struct BlobDispatcherWithChannelStatus<ChannelDispatch, ChannelStatus>(
PhantomData<(ChannelDispatch, ChannelStatus)>,
);
impl<ChannelDispatch: DispatchBlob, ChannelStatus> DispatchBlob
for BlobDispatcherWithChannelStatus<ChannelDispatch, ChannelStatus>
{
fn dispatch_blob(blob: Vec<u8>) -> Result<(), DispatchBlobError> {
ChannelDispatch::dispatch_blob(blob)
}
}
impl<ChannelDispatch, ChannelStatus: DispatchChannelStatusProvider> DispatchChannelStatusProvider
for BlobDispatcherWithChannelStatus<ChannelDispatch, ChannelStatus>
{
fn is_congested(with: &Location) -> bool {
ChannelStatus::is_congested(with)
}
}