// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see .
//! XCM sender for relay chain.
use alloc::vec::Vec;
use codec::{DecodeLimit, Encode};
use core::marker::PhantomData;
use pezframe_support::traits::Get;
use pezframe_system::pezpallet_prelude::BlockNumberFor;
use pezkuwi_primitives::Id as ParaId;
use pezkuwi_runtime_teyrchains::{
configuration::{self, HostConfiguration},
dmp, FeeTracker,
};
use pezsp_runtime::FixedPointNumber;
use xcm::{prelude::*, MAX_XCM_DECODE_DEPTH};
use xcm_builder::InspectMessageQueues;
use SendError::*;
/// Simple value-bearing trait for determining/expressing the assets required to be paid for a
/// messages to be delivered to a teyrchain.
pub trait PriceForMessageDelivery {
/// Type used for charging different prices to different destinations
type Id;
/// Return the assets required to deliver `message` to the given `para` destination.
fn price_for_delivery(id: Self::Id, message: &Xcm<()>) -> Assets;
}
impl PriceForMessageDelivery for () {
type Id = ();
fn price_for_delivery(_: Self::Id, _: &Xcm<()>) -> Assets {
Assets::new()
}
}
pub struct NoPriceForMessageDelivery(PhantomData);
impl PriceForMessageDelivery for NoPriceForMessageDelivery {
type Id = Id;
fn price_for_delivery(_: Self::Id, _: &Xcm<()>) -> Assets {
Assets::new()
}
}
/// Implementation of [`PriceForMessageDelivery`] which returns a fixed price.
pub struct ConstantPrice(core::marker::PhantomData);
impl> PriceForMessageDelivery for ConstantPrice {
type Id = ();
fn price_for_delivery(_: Self::Id, _: &Xcm<()>) -> Assets {
T::get()
}
}
/// Implementation of [`PriceForMessageDelivery`] which returns an exponentially increasing price.
/// The formula for the fee is based on the sum of a base fee plus a message length fee, multiplied
/// by a specified factor. In mathematical form:
///
/// `F * (B + encoded_msg_len * M)`
///
/// Thus, if F = 1 and M = 0, this type is equivalent to [`ConstantPrice`].
///
/// The type parameters are understood as follows:
///
/// - `A`: Used to denote the asset ID that will be used for paying the delivery fee.
/// - `B`: The base fee to pay for message delivery.
/// - `M`: The fee to pay for each and every byte of the message after encoding it.
/// - `F`: A fee factor multiplier. It can be understood as the exponent term in the formula.
pub struct ExponentialPrice(core::marker::PhantomData<(A, B, M, F)>);
impl, B: Get, M: Get, F: FeeTracker> PriceForMessageDelivery
for ExponentialPrice
{
type Id = F::Id;
fn price_for_delivery(id: Self::Id, msg: &Xcm<()>) -> Assets {
let msg_fee = (msg.encoded_size() as u128).saturating_mul(M::get());
let fee_sum = B::get().saturating_add(msg_fee);
let amount = F::get_fee_factor(id).saturating_mul_int(fee_sum);
(A::get(), amount).into()
}
}
/// XCM sender for relay chain. It only sends downward message.
pub struct ChildTeyrchainRouter(PhantomData<(T, W, P)>);
impl SendXcm
for ChildTeyrchainRouter
where
P: PriceForMessageDelivery,
{
type Ticket = (HostConfiguration>, ParaId, Vec);
fn validate(
dest: &mut Option,
msg: &mut Option>,
) -> SendResult<(HostConfiguration>, ParaId, Vec)> {
let d = dest.take().ok_or(MissingArgument)?;
let id = if let (0, [Teyrchain(id)]) = d.unpack() {
*id
} else {
*dest = Some(d);
return Err(NotApplicable);
};
// Downward message passing.
let xcm = msg.take().ok_or(MissingArgument)?;
let config = configuration::ActiveConfig::::get();
let para = id.into();
let price = P::price_for_delivery(para, &xcm);
let versioned_xcm = W::wrap_version(&d, xcm).map_err(|()| DestinationUnsupported)?;
versioned_xcm.check_is_decodable().map_err(|()| ExceedsMaxMessageSize)?;
let blob = versioned_xcm.encode();
dmp::Pezpallet::::can_queue_downward_message(&config, ¶, &blob)
.map_err(Into::::into)?;
Ok(((config, para, blob), price))
}
fn deliver(
(config, para, blob): (HostConfiguration>, ParaId, Vec),
) -> Result {
let hash = pezsp_io::hashing::blake2_256(&blob[..]);
dmp::Pezpallet::::queue_downward_message(&config, para, blob)
.map(|()| hash)
.map_err(|error| {
log::debug!(
target: "xcm::xcm_sender::deliver",
"Failed to place into DMP queue: error: {error:?}, id: {hash:?}",
);
SendError::Transport(&"Error placing into DMP queue")
})
}
#[cfg(feature = "runtime-benchmarks")]
fn ensure_successful_delivery(location: Option) {
if let Some((0, [Teyrchain(id)])) = location.as_ref().map(|l| l.unpack()) {
dmp::Pezpallet::::make_teyrchain_reachable(*id);
}
}
}
impl InspectMessageQueues for ChildTeyrchainRouter {
fn clear_messages() {
// Best effort.
let _ = dmp::DownwardMessageQueues::::clear(u32::MAX, None);
}
fn get_messages() -> Vec<(VersionedLocation, Vec>)> {
dmp::DownwardMessageQueues::::iter()
.map(|(para_id, messages)| {
let decoded_messages: Vec> = messages
.iter()
.map(|downward_message| {
let message = VersionedXcm::<()>::decode_all_with_depth_limit(
MAX_XCM_DECODE_DEPTH,
&mut &downward_message.msg[..],
)
.unwrap();
log::trace!(
target: "xcm::DownwardMessageQueues::get_messages",
"Message: {:?}, sent at: {:?}", message, downward_message.sent_at
);
message
})
.collect();
(
VersionedLocation::from(Location::from(Teyrchain(para_id.into()))),
decoded_messages,
)
})
.collect()
}
}
/// Implementation of `xcm_builder::EnsureDelivery` which helps to ensure delivery to the
/// `ParaId` teyrchain (sibling or child). Deposits existential deposit for origin (if needed).
/// Deposits estimated fee to the origin account (if needed).
/// Allows to trigger additional logic for specific `ParaId` (e.g. open HRMP channel) (if needed).
#[cfg(feature = "runtime-benchmarks")]
pub struct ToTeyrchainDeliveryHelper<
XcmConfig,
ExistentialDeposit,
PriceForDelivery,
ParaId,
ToParaIdHelper,
>(
core::marker::PhantomData<(
XcmConfig,
ExistentialDeposit,
PriceForDelivery,
ParaId,
ToParaIdHelper,
)>,
);
#[cfg(feature = "runtime-benchmarks")]
impl<
XcmConfig: xcm_executor::Config,
ExistentialDeposit: Get