Newer
Older
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Substrate 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.
// Substrate 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
//! A pallet which uses the XCMP transport layer to handle both incoming and outgoing XCM message
//! sending and dispatch, queuing, signalling and backpressure. To do so, it implements:
//! * `XcmpMessageHandler`
//! * `XcmpMessageSource`
//!
//! Also provides an implementation of `SendXcm` which can be placed in a router tuple for relaying
//! XCM over XCMP if the destination is `Parent/Parachain`. It requires an implementation of
//! `XcmExecutor` for dispatching incoming XCM messages.
//!
//! To prevent out of memory errors on the `OutboundXcmpMessages` queue, an exponential fee factor
//! (`DeliveryFeeFactor`) is set, much like the one used in DMP.
//! The fee factor increases whenever the total size of messages in a particular channel passes a
//! threshold. This threshold is defined as a percentage of the maximum total size the channel can
//! have. More concretely, the threshold is `max_total_size` / `THRESHOLD_FACTOR`, where:
//! - `max_total_size` is the maximum size, in bytes, of the channel, not number of messages.
//! It is defined in the channel configuration.
//! - `THRESHOLD_FACTOR` just declares which percentage of the max size is the actual threshold.
//! If it's 2, then the threshold is half of the max size, if it's 4, it's a quarter, and so on.
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
Adrian Catangiu
committed
#[cfg(feature = "bridging")]
pub mod bridging;
pub mod weights;
pub use weights::WeightInfo;
use codec::{Decode, DecodeLimit, Encode};
relay_chain::BlockNumber as RelayBlockNumber, ChannelStatus, GetChannelInfo, MessageSendError,
ParaId, XcmpMessageFormat, XcmpMessageHandler, XcmpMessageSource,
use frame_support::{
weights::{constants::WEIGHT_REF_TIME_PER_MILLIS, Weight},
};
use polkadot_runtime_common::xcm_sender::PriceForMessageDelivery;
use polkadot_runtime_parachains::FeeTracker;
use rand_chacha::{
rand_core::{RngCore, SeedableRng},
ChaChaRng,
use sp_core::MAX_POSSIBLE_ALLOCATION;
use sp_runtime::{FixedU128, RuntimeDebug, Saturating};
use sp_std::{convert::TryFrom, prelude::*};
use xcm::{latest::prelude::*, VersionedXcm, WrapVersion, MAX_XCM_DECODE_DEPTH};
use xcm_executor::traits::ConvertOrigin;
/// Index used to identify overweight XCMs.
pub type OverweightIndex = u64;
const LOG_TARGET: &str = "xcmp_queue";
const DEFAULT_POV_SIZE: u64 = 64 * 1024; // 64 KB
/// Constants related to delivery fee calculation
pub mod delivery_fee_constants {
use super::FixedU128;
/// Fees will start increasing when queue is half full
pub const THRESHOLD_FACTOR: u32 = 2;
/// The base number the delivery fee factor gets multiplied by every time it is increased.
/// Also, the number it gets divided by when decreased.
pub const EXPONENTIAL_FEE_BASE: FixedU128 = FixedU128::from_rational(105, 100); // 1.05
/// The contribution of each KB to a fee factor increase
pub const MESSAGE_SIZE_FEE_BASE: FixedU128 = FixedU128::from_rational(1, 1000); // 0.001
}
// Maximum amount of messages to process per block. This is a temporary measure until we properly
// account for proof size weights.
const MAX_MESSAGES_PER_BLOCK: u8 = 10;
// Maximum amount of messages that can exist in the overweight queue at any given time.
const MAX_OVERWEIGHT_MESSAGES: u32 = 1000;
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::{pallet_prelude::*, Twox64Concat};
use frame_system::pallet_prelude::*;
#[pallet::pallet]
#[pallet::storage_version(migration::STORAGE_VERSION)]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// Something to execute an XCM message. We need this to service the XCMoXCMP queue.
type XcmExecutor: ExecuteXcm<Self::RuntimeCall>;
/// Information on the avaialble XCMP channels.
type ChannelInfo: GetChannelInfo;
/// Means of converting an `Xcm` into a `VersionedXcm`.
type VersionWrapper: WrapVersion;
/// The origin that is allowed to execute overweight messages.
type ExecuteOverweightOrigin: EnsureOrigin<Self::RuntimeOrigin>;
/// The origin that is allowed to resume or suspend the XCMP queue.
type ControllerOrigin: EnsureOrigin<Self::RuntimeOrigin>;
/// The conversion function used to attempt to convert an XCM `MultiLocation` origin to a
/// superuser origin.
type ControllerOriginConverter: ConvertOrigin<Self::RuntimeOrigin>;
/// The price for delivering an XCM to a sibling parachain destination.
type PriceForSiblingDelivery: PriceForMessageDelivery<Id = ParaId>;
/// The weight information of this pallet.
type WeightInfo: WeightInfo;
}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_idle(_now: BlockNumberFor<T>, max_weight: Weight) -> Weight {
// on_idle processes additional messages with any remaining block weight.
Self::service_xcmp_queue(max_weight)
}
}
impl<T: Config> Pallet<T> {
/// Services a single overweight XCM.
///
/// - `origin`: Must pass `ExecuteOverweightOrigin`.
/// - `index`: The index of the overweight XCM to service
/// - `weight_limit`: The amount of weight that XCM execution may take.
///
/// Errors:
/// - `BadOverweightIndex`: XCM under `index` is not found in the `Overweight` storage map.
/// - `BadXcm`: XCM under `index` cannot be properly decoded into a valid XCM format.
/// - `WeightOverLimit`: XCM execution may use greater `weight_limit`.
///
/// Events:
/// - `OverweightServiced`: On success.
#[pallet::call_index(0)]
#[pallet::weight((weight_limit.saturating_add(Weight::from_parts(1_000_000, 0)), DispatchClass::Operational))]
pub fn service_overweight(
origin: OriginFor<T>,
index: OverweightIndex,
) -> DispatchResultWithPostInfo {
T::ExecuteOverweightOrigin::ensure_origin(origin)?;
let (sender, sent_at, data) =
Overweight::<T>::get(index).ok_or(Error::<T>::BadOverweightIndex)?;
let xcm = VersionedXcm::<T::RuntimeCall>::decode_all_with_depth_limit(
MAX_XCM_DECODE_DEPTH,
&mut data.as_slice(),
)
.map_err(|_| Error::<T>::BadXcm)?;
let used = Self::handle_xcm_message(sender, sent_at, xcm, weight_limit)
.map_err(|_| Error::<T>::WeightOverLimit)?;
Overweight::<T>::remove(index);
Self::deposit_event(Event::OverweightServiced { index, used });
Ok(Some(used.saturating_add(Weight::from_parts(1_000_000, 0))).into())
/// Suspends all XCM executions for the XCMP queue, regardless of the sender's origin.
///
/// - `origin`: Must pass `ControllerOrigin`.
#[pallet::call_index(1)]
#[pallet::weight((T::DbWeight::get().writes(1), DispatchClass::Operational,))]
pub fn suspend_xcm_execution(origin: OriginFor<T>) -> DispatchResult {
T::ControllerOrigin::ensure_origin(origin)?;
QueueSuspended::<T>::put(true);
Ok(())
}
Loading full blame...