// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot 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. // Polkadot 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 Polkadot. If not, see . //! XCM sender for relay chain. use frame_support::traits::Get; use parity_scale_codec::Encode; use primitives::Id as ParaId; use runtime_parachains::{ configuration::{self, HostConfiguration}, dmp, FeeTracker, }; use sp_runtime::FixedPointNumber; use sp_std::{marker::PhantomData, prelude::*}; use xcm::prelude::*; use SendError::*; /// Simple value-bearing trait for determining/expressing the assets required to be paid for a /// messages to be delivered to a parachain. pub trait PriceForParachainDelivery { /// Return the assets required to deliver `message` to the given `para` destination. fn price_for_parachain_delivery(para: ParaId, message: &Xcm<()>) -> MultiAssets; } impl PriceForParachainDelivery for () { fn price_for_parachain_delivery(_: ParaId, _: &Xcm<()>) -> MultiAssets { MultiAssets::new() } } /// Implementation of `PriceForParachainDelivery` which returns a fixed price. pub struct ConstantPrice(sp_std::marker::PhantomData); impl> PriceForParachainDelivery for ConstantPrice { fn price_for_parachain_delivery(_: ParaId, _: &Xcm<()>) -> MultiAssets { T::get() } } /// Implementation of `PriceForParachainDelivery` which returns an exponentially increasing price. /// The `A` type parameter is used to denote the asset ID that will be used for paying the delivery /// fee. /// /// 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, it is `F * (B + encoded_msg_len * M)`. pub struct ExponentialPrice(sp_std::marker::PhantomData<(A, B, M, F)>); impl, B: Get, M: Get, F: FeeTracker> PriceForParachainDelivery for ExponentialPrice { fn price_for_parachain_delivery(para: ParaId, msg: &Xcm<()>) -> MultiAssets { 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(para).saturating_mul_int(fee_sum); (A::get(), amount).into() } } /// XCM sender for relay chain. It only sends downward message. pub struct ChildParachainRouter(PhantomData<(T, W, P)>); impl SendXcm for ChildParachainRouter { 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 MultiLocation { parents: 0, interior: X1(Parachain(id)) } = &d { *id } else { *dest = Some(d); return Err(NotApplicable) }; // Downward message passing. let xcm = msg.take().ok_or(MissingArgument)?; let config = >::config(); let para = id.into(); let price = P::price_for_parachain_delivery(para, &xcm); let blob = W::wrap_version(&d, xcm).map_err(|()| DestinationUnsupported)?.encode(); >::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 = sp_io::hashing::blake2_256(&blob[..]); >::queue_downward_message(&config, para, blob) .map(|()| hash) .map_err(|_| SendError::Transport(&"Error placing into DMP queue")) } } #[cfg(test)] mod tests { use super::*; use frame_support::parameter_types; use runtime_parachains::FeeTracker; use sp_runtime::FixedU128; parameter_types! { pub const BaseDeliveryFee: u128 = 300_000_000; pub const TransactionByteFee: u128 = 1_000_000; pub FeeAssetId: AssetId = Concrete(Here.into()); } struct TestFeeTracker; impl FeeTracker for TestFeeTracker { fn get_fee_factor(_: ParaId) -> FixedU128 { FixedU128::from_rational(101, 100) } } type TestExponentialPrice = ExponentialPrice; #[test] fn exponential_price_correct_price_calculation() { let id: ParaId = 123.into(); let b: u128 = BaseDeliveryFee::get(); let m: u128 = TransactionByteFee::get(); // F * (B + msg_length * M) // message_length = 1 let result: u128 = TestFeeTracker::get_fee_factor(id.clone()).saturating_mul_int(b + m); assert_eq!( TestExponentialPrice::price_for_parachain_delivery(id.clone(), &Xcm(vec![])), (FeeAssetId::get(), result).into() ); // message size = 2 let result: u128 = TestFeeTracker::get_fee_factor(id.clone()).saturating_mul_int(b + (2 * m)); assert_eq!( TestExponentialPrice::price_for_parachain_delivery(id.clone(), &Xcm(vec![ClearOrigin])), (FeeAssetId::get(), result).into() ); // message size = 4 let result: u128 = TestFeeTracker::get_fee_factor(id.clone()).saturating_mul_int(b + (4 * m)); assert_eq!( TestExponentialPrice::price_for_parachain_delivery( id.clone(), &Xcm(vec![SetAppendix(Xcm(vec![ClearOrigin]))]) ), (FeeAssetId::get(), result).into() ); } }