lib.rs 116 KiB
Newer Older
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
Gavin Wood's avatar
Gavin Wood committed
// 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,
Gavin Wood's avatar
Gavin Wood committed
// 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 <http://www.gnu.org/licenses/>.
Gavin Wood's avatar
Gavin Wood committed

//! Pallet to handle XCM messages.

#![cfg_attr(not(feature = "std"), no_std)]

Gavin Wood's avatar
Gavin Wood committed
#[cfg(feature = "runtime-benchmarks")]
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;

Gavin Wood's avatar
Gavin Wood committed
pub mod migration;

use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
	dispatch::{DispatchErrorWithPostInfo, GetDispatchInfo, WithPostDispatchInfo},
	pallet_prelude::*,
	traits::{
		Contains, ContainsPair, Currency, Defensive, EnsureOrigin, Get, LockableCurrency,
		OriginTrait, WithdrawReasons,
	},
	PalletId,
Gavin Wood's avatar
Gavin Wood committed
};
use frame_system::pallet_prelude::{BlockNumberFor, *};
pub use pallet::*;
use sp_runtime::{
Gavin Wood's avatar
Gavin Wood committed
	traits::{
		AccountIdConversion, BadOrigin, BlakeTwo256, BlockNumberProvider, Dispatchable, Hash,
		Saturating, Zero,
Gavin Wood's avatar
Gavin Wood committed
	},
use sp_std::{boxed::Box, marker::PhantomData, prelude::*, result::Result, vec};
Gavin Wood's avatar
Gavin Wood committed
use xcm::{latest::QueryResponseInfo, prelude::*};
	ExecuteController, ExecuteControllerWeightInfo, QueryController, QueryControllerWeightInfo,
	SendController, SendControllerWeightInfo,
Gavin Wood's avatar
Gavin Wood committed
};
use xcm_executor::{
	traits::{
		AssetTransferError, CheckSuspension, ClaimAssets, ConvertLocation, ConvertOrigin,
		DropAssets, MatchesFungible, OnResponse, Properties, QueryHandler, QueryResponseStatus,
		TransactAsset, TransferType, VersionChangeNotifier, WeightBounds, XcmAssetTransfers,
Gavin Wood's avatar
Gavin Wood committed
	},
Francisco Aguirre's avatar
Francisco Aguirre committed
	AssetsInHolding,
Gavin Wood's avatar
Gavin Wood committed
};
use xcm_fee_payment_runtime_api::Error as FeePaymentError;
Gavin Wood's avatar
Gavin Wood committed

#[cfg(any(feature = "try-runtime", test))]
use sp_runtime::TryRuntimeError;

Gavin Wood's avatar
Gavin Wood committed
pub trait WeightInfo {
	fn send() -> Weight;
	fn teleport_assets() -> Weight;
	fn reserve_transfer_assets() -> Weight;
Gavin Wood's avatar
Gavin Wood committed
	fn execute() -> Weight;
	fn force_xcm_version() -> Weight;
	fn force_default_xcm_version() -> Weight;
	fn force_subscribe_version_notify() -> Weight;
	fn force_unsubscribe_version_notify() -> Weight;
	fn force_suspension() -> Weight;
Gavin Wood's avatar
Gavin Wood committed
	fn migrate_supported_version() -> Weight;
	fn migrate_version_notifiers() -> Weight;
	fn already_notified_target() -> Weight;
	fn notify_current_targets() -> Weight;
	fn notify_target_migration_fail() -> Weight;
	fn migrate_version_notify_targets() -> Weight;
	fn migrate_and_notify_old_targets() -> Weight;
	fn new_query() -> Weight;
	fn take_response() -> Weight;
	fn claim_assets() -> Weight;
Gavin Wood's avatar
Gavin Wood committed
}

/// fallback implementation
pub struct TestWeightInfo;
impl WeightInfo for TestWeightInfo {
	fn send() -> Weight {
		Weight::from_parts(100_000_000, 0)
Gavin Wood's avatar
Gavin Wood committed
	}

	fn teleport_assets() -> Weight {
		Weight::from_parts(100_000_000, 0)
Gavin Wood's avatar
Gavin Wood committed
	}

	fn reserve_transfer_assets() -> Weight {
		Weight::from_parts(100_000_000, 0)
Gavin Wood's avatar
Gavin Wood committed
	}

	fn transfer_assets() -> Weight {
		Weight::from_parts(100_000_000, 0)
	}

Gavin Wood's avatar
Gavin Wood committed
	fn execute() -> Weight {
		Weight::from_parts(100_000_000, 0)
Gavin Wood's avatar
Gavin Wood committed
	}

	fn force_xcm_version() -> Weight {
		Weight::from_parts(100_000_000, 0)
Gavin Wood's avatar
Gavin Wood committed
	}

	fn force_default_xcm_version() -> Weight {
		Weight::from_parts(100_000_000, 0)
Gavin Wood's avatar
Gavin Wood committed
	}

	fn force_subscribe_version_notify() -> Weight {
		Weight::from_parts(100_000_000, 0)
Gavin Wood's avatar
Gavin Wood committed
	}

	fn force_unsubscribe_version_notify() -> Weight {
		Weight::from_parts(100_000_000, 0)
Gavin Wood's avatar
Gavin Wood committed
	}

	fn force_suspension() -> Weight {
		Weight::from_parts(100_000_000, 0)
	}

Gavin Wood's avatar
Gavin Wood committed
	fn migrate_supported_version() -> Weight {
		Weight::from_parts(100_000_000, 0)
Gavin Wood's avatar
Gavin Wood committed
	}

	fn migrate_version_notifiers() -> Weight {
		Weight::from_parts(100_000_000, 0)
Gavin Wood's avatar
Gavin Wood committed
	}

	fn already_notified_target() -> Weight {
		Weight::from_parts(100_000_000, 0)
Gavin Wood's avatar
Gavin Wood committed
	}

	fn notify_current_targets() -> Weight {
		Weight::from_parts(100_000_000, 0)
Gavin Wood's avatar
Gavin Wood committed
	}

	fn notify_target_migration_fail() -> Weight {
		Weight::from_parts(100_000_000, 0)
Gavin Wood's avatar
Gavin Wood committed
	}

	fn migrate_version_notify_targets() -> Weight {
		Weight::from_parts(100_000_000, 0)
Gavin Wood's avatar
Gavin Wood committed
	}

	fn migrate_and_notify_old_targets() -> Weight {
		Weight::from_parts(100_000_000, 0)
Gavin Wood's avatar
Gavin Wood committed
	}

	fn new_query() -> Weight {
		Weight::from_parts(100_000_000, 0)
	}

	fn take_response() -> Weight {
		Weight::from_parts(100_000_000, 0)
	}

	fn claim_assets() -> Weight {
		Weight::from_parts(100_000_000, 0)
	}
Gavin Wood's avatar
Gavin Wood committed
}
Gavin Wood's avatar
Gavin Wood committed

#[frame_support::pallet]
pub mod pallet {
	use super::*;
		dispatch::{GetDispatchInfo, PostDispatchInfo},
		parameter_types,
Gavin Wood's avatar
Gavin Wood committed
	use frame_system::Config as SysConfig;
	use sp_runtime::traits::Dispatchable;
Gavin Wood's avatar
Gavin Wood committed
	use xcm_executor::traits::{MatchesFungible, WeightBounds};
	parameter_types! {
		/// An implementation of `Get<u32>` which just returns the latest XCM version which we can
		/// support.
		pub const CurrentXcmVersion: u32 = XCM_VERSION;
	}

	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);

Gavin Wood's avatar
Gavin Wood committed
	#[pallet::pallet]
	#[pallet::storage_version(STORAGE_VERSION)]
	#[pallet::without_storage_info]
Gavin Wood's avatar
Gavin Wood committed
	pub struct Pallet<T>(_);

Gavin Wood's avatar
Gavin Wood committed
	pub type BalanceOf<T> =
		<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;

Gavin Wood's avatar
Gavin Wood committed
	#[pallet::config]
	/// The module configuration trait.
	pub trait Config: frame_system::Config {
		/// The overarching event type.
		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
Gavin Wood's avatar
Gavin Wood committed
		/// A lockable currency.
		// TODO: We should really use a trait which can handle multiple currencies.
		type Currency: LockableCurrency<Self::AccountId, Moment = BlockNumberFor<Self>>;
Gavin Wood's avatar
Gavin Wood committed

Francisco Aguirre's avatar
Francisco Aguirre committed
		/// The `Asset` matcher for `Currency`.
Gavin Wood's avatar
Gavin Wood committed
		type CurrencyMatcher: MatchesFungible<BalanceOf<Self>>;

Francisco Aguirre's avatar
Francisco Aguirre committed
		/// Required origin for sending XCM messages. If successful, it resolves to `Location`
Gavin Wood's avatar
Gavin Wood committed
		/// which exists as an interior location within this chain's XCM context.
Francisco Aguirre's avatar
Francisco Aguirre committed
		type SendXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
Gavin Wood's avatar
Gavin Wood committed

		/// The type used to actually dispatch an XCM to its destination.
		type XcmRouter: SendXcm;

		/// Required origin for executing XCM messages, including the teleport functionality. If
Francisco Aguirre's avatar
Francisco Aguirre committed
		/// successful, then it resolves to `Location` which exists as an interior location
		/// within this chain's XCM context.
Francisco Aguirre's avatar
Francisco Aguirre committed
		type ExecuteXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
		/// Our XCM filter which messages to be executed using `XcmExecutor` must pass.
Francisco Aguirre's avatar
Francisco Aguirre committed
		type XcmExecuteFilter: Contains<(Location, Xcm<<Self as Config>::RuntimeCall>)>;
Gavin Wood's avatar
Gavin Wood committed
		/// Something to execute an XCM message.
		type XcmExecutor: ExecuteXcm<<Self as Config>::RuntimeCall> + XcmAssetTransfers;
		/// Our XCM filter which messages to be teleported using the dedicated extrinsic must pass.
Francisco Aguirre's avatar
Francisco Aguirre committed
		type XcmTeleportFilter: Contains<(Location, Vec<Asset>)>;
		/// Our XCM filter which messages to be reserve-transferred using the dedicated extrinsic
		/// must pass.
Francisco Aguirre's avatar
Francisco Aguirre committed
		type XcmReserveTransferFilter: Contains<(Location, Vec<Asset>)>;
		/// Means of measuring the weight consumed by an XCM message locally.
		type Weigher: WeightBounds<<Self as Config>::RuntimeCall>;
Gavin Wood's avatar
Gavin Wood committed
		/// This chain's Universal Location.
Francisco Aguirre's avatar
Francisco Aguirre committed
		type UniversalLocation: Get<InteriorLocation>;
Gavin Wood's avatar
Gavin Wood committed
		/// The runtime `Origin` type.
Sergej Sakac's avatar
Sergej Sakac committed
		type RuntimeOrigin: From<Origin> + From<<Self as SysConfig>::RuntimeOrigin>;
Gavin Wood's avatar
Gavin Wood committed
		/// The runtime `Call` type.
		type RuntimeCall: Parameter
Sergej Sakac's avatar
Sergej Sakac committed
			+ Dispatchable<
				RuntimeOrigin = <Self as Config>::RuntimeOrigin,
				PostInfo = PostDispatchInfo,
			>;

		const VERSION_DISCOVERY_QUEUE_SIZE: u32;

		/// The latest supported version that we advertise. Generally just set it to
		/// `pallet_xcm::CurrentXcmVersion`.
		type AdvertisedXcmVersion: Get<XcmVersion>;
		/// The origin that is allowed to call privileged operations on the XCM pallet
		type AdminOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin>;

Gavin Wood's avatar
Gavin Wood committed
		/// The assets which we consider a given origin is trusted if they claim to have placed a
		/// lock.
Francisco Aguirre's avatar
Francisco Aguirre committed
		type TrustedLockers: ContainsPair<Location, Asset>;
Gavin Wood's avatar
Gavin Wood committed

Francisco Aguirre's avatar
Francisco Aguirre committed
		/// How to get an `AccountId` value from a `Location`, useful for handling asset locks.
		type SovereignAccountOf: ConvertLocation<Self::AccountId>;
Gavin Wood's avatar
Gavin Wood committed

		/// The maximum number of local XCM locks that a single account may have.
		type MaxLockers: Get<u32>;

		/// The maximum number of consumers a single remote lock may have.
		type MaxRemoteLockConsumers: Get<u32>;

		/// The ID type for local consumers of remote locks.
		type RemoteLockConsumerIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy;

Gavin Wood's avatar
Gavin Wood committed
		/// Weight information for extrinsics in this pallet.
		type WeightInfo: WeightInfo;
	}
	impl<T: Config> ExecuteControllerWeightInfo for Pallet<T> {
		fn execute() -> Weight {
			T::WeightInfo::execute()
		}
	}

	impl<T: Config> ExecuteController<OriginFor<T>, <T as Config>::RuntimeCall> for Pallet<T> {
		type WeightInfo = Self;
			message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
		) -> Result<Weight, DispatchErrorWithPostInfo> {
			log::trace!(target: "xcm::pallet_xcm::execute", "message {:?}, max_weight {:?}", message, max_weight);
			let outcome = (|| {
				let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
				let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
				let message = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
				let value = (origin_location, message);
				ensure!(T::XcmExecuteFilter::contains(&value), Error::<T>::Filtered);
				let (origin_location, message) = value;
				Ok(T::XcmExecutor::prepare_and_execute(
					origin_location,
					message,
					&mut hash,
					max_weight,
					max_weight,
				))
			})()
			.map_err(|e: DispatchError| {
				e.with_weight(<Self::WeightInfo as ExecuteControllerWeightInfo>::execute())
			})?;

			Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
			let weight_used = outcome.weight_used();
			outcome.ensure_complete().map_err(|error| {
				log::error!(target: "xcm::pallet_xcm::execute", "XCM execution failed with error {:?}", error);
				Error::<T>::LocalExecutionIncomplete.with_weight(
					weight_used.saturating_add(
						<Self::WeightInfo as ExecuteControllerWeightInfo>::execute(),
					),
				)
			})?;
			Ok(weight_used)
		}
	}

	impl<T: Config> SendControllerWeightInfo for Pallet<T> {
		fn send() -> Weight {
			T::WeightInfo::send()
		}
	}

	impl<T: Config> SendController<OriginFor<T>> for Pallet<T> {
		type WeightInfo = Self;
Francisco Aguirre's avatar
Francisco Aguirre committed
			dest: Box<VersionedLocation>,
			message: Box<VersionedXcm<()>>,
		) -> Result<XcmHash, DispatchError> {
			let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
			let interior: Junctions =
				origin_location.clone().try_into().map_err(|_| Error::<T>::InvalidOrigin)?;
			let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
			let message: Xcm<()> = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;

			let message_id = Self::send_xcm(interior, dest.clone(), message.clone())
				.map_err(Error::<T>::from)?;
			let e = Event::Sent { origin: origin_location, destination: dest, message, message_id };
			Self::deposit_event(e);
			Ok(message_id)
		}
	}

	impl<T: Config> QueryControllerWeightInfo for Pallet<T> {
		fn query() -> Weight {
			T::WeightInfo::new_query()
		}
		fn take_response() -> Weight {
			T::WeightInfo::take_response()
		}
	}

	impl<T: Config> QueryController<OriginFor<T>, BlockNumberFor<T>> for Pallet<T> {
		type WeightInfo = Self;

		fn query(
			origin: OriginFor<T>,
			timeout: BlockNumberFor<T>,
Francisco Aguirre's avatar
Francisco Aguirre committed
			match_querier: VersionedLocation,
		) -> Result<QueryId, DispatchError> {
			let responder = <T as Config>::ExecuteXcmOrigin::ensure_origin(origin)?;
			let query_id = <Self as QueryHandler>::new_query(
				responder,
				timeout,
Francisco Aguirre's avatar
Francisco Aguirre committed
				Location::try_from(match_querier)
					.map_err(|_| Into::<DispatchError>::into(Error::<T>::BadVersion))?,
			);

			Ok(query_id)
		}
	}

Gavin Wood's avatar
Gavin Wood committed
	#[pallet::event]
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
	pub enum Event<T: Config> {
		/// Execution of an XCM message was attempted.
		Attempted { outcome: xcm::latest::Outcome },
Francisco Aguirre's avatar
Francisco Aguirre committed
		Sent { origin: Location, destination: Location, message: Xcm<()>, message_id: XcmHash },
		/// Query response received which does not match a registered query. This may be because a
		/// matching query was never registered, it may be because it is a duplicate response, or
		/// because the query timed out.
Francisco Aguirre's avatar
Francisco Aguirre committed
		UnexpectedResponse { origin: Location, query_id: QueryId },
		/// Query response has been received and is ready for taking with `take_response`. There is
		/// no registered notification call.
		ResponseReady { query_id: QueryId, response: Response },
		/// Query response has been received and query is removed. The registered notification has
		/// been dispatched and executed successfully.
		Notified { query_id: QueryId, pallet_index: u8, call_index: u8 },
		/// Query response has been received and query is removed. The registered notification
		/// could not be dispatched because the dispatch weight is greater than the maximum weight
		/// originally budgeted by this runtime for the query result.
		NotifyOverweight {
			query_id: QueryId,
			pallet_index: u8,
			call_index: u8,
			actual_weight: Weight,
			max_budgeted_weight: Weight,
		},
		/// Query response has been received and query is removed. There was a general error with
		/// dispatching the notification call.
		NotifyDispatchError { query_id: QueryId, pallet_index: u8, call_index: u8 },
		/// Query response has been received and query is removed. The dispatch was unable to be
		/// decoded into a `Call`; this might be due to dispatch function having a signature which
		/// is not `(origin, QueryId, Response)`.
		NotifyDecodeFailed { query_id: QueryId, pallet_index: u8, call_index: u8 },
		/// Expected query response has been received but the origin location of the response does
		/// not match that expected. The query remains registered for a later, valid, response to
		/// be received and acted upon.
Francisco Aguirre's avatar
Francisco Aguirre committed
			origin: Location,
Francisco Aguirre's avatar
Francisco Aguirre committed
			expected_location: Option<Location>,
		/// Expected query response has been received but the expected origin location placed in
		/// storage by this runtime previously cannot be decoded. The query remains registered.
		///
		/// This is unexpected (since a location placed in storage in a previously executing
		/// runtime should be readable prior to query timeout) and dangerous since the possibly
		/// valid response will be dropped. Manual governance intervention is probably going to be
		/// needed.
Francisco Aguirre's avatar
Francisco Aguirre committed
		InvalidResponderVersion { origin: Location, query_id: QueryId },
		/// Received query response has been read and removed.
		ResponseTaken { query_id: QueryId },
		/// Some assets have been placed in an asset trap.
Francisco Aguirre's avatar
Francisco Aguirre committed
		AssetsTrapped { hash: H256, origin: Location, assets: VersionedAssets },
		/// An XCM version change notification message has been attempted to be sent.
		///
Gavin Wood's avatar
Gavin Wood committed
		/// The cost of sending it (borne by the chain) is included.
		VersionChangeNotified {
Francisco Aguirre's avatar
Francisco Aguirre committed
			destination: Location,
			result: XcmVersion,
Francisco Aguirre's avatar
Francisco Aguirre committed
			cost: Assets,
		/// The supported version of a location has been changed. This might be through an
		/// automatic notification or a manual intervention.
Francisco Aguirre's avatar
Francisco Aguirre committed
		SupportedVersionChanged { location: Location, version: XcmVersion },
		/// A given location which had a version change subscription was dropped owing to an error
		/// sending the notification to it.
Francisco Aguirre's avatar
Francisco Aguirre committed
		NotifyTargetSendFail { location: Location, query_id: QueryId, error: XcmError },
		/// A given location which had a version change subscription was dropped owing to an error
		/// migrating the location to our new XCM format.
Francisco Aguirre's avatar
Francisco Aguirre committed
		NotifyTargetMigrationFail { location: VersionedLocation, query_id: QueryId },
Gavin Wood's avatar
Gavin Wood committed
		/// Expected query response has been received but the expected querier location placed in
		/// storage by this runtime previously cannot be decoded. The query remains registered.
		///
		/// This is unexpected (since a location placed in storage in a previously executing
		/// runtime should be readable prior to query timeout) and dangerous since the possibly
		/// valid response will be dropped. Manual governance intervention is probably going to be
		/// needed.
Francisco Aguirre's avatar
Francisco Aguirre committed
		InvalidQuerierVersion { origin: Location, query_id: QueryId },
Gavin Wood's avatar
Gavin Wood committed
		/// Expected query response has been received but the querier location of the response does
		/// not match the expected. The query remains registered for a later, valid, response to
		/// be received and acted upon.
Francisco Aguirre's avatar
Francisco Aguirre committed
			origin: Location,
Francisco Aguirre's avatar
Francisco Aguirre committed
			expected_querier: Location,
			maybe_actual_querier: Option<Location>,
Gavin Wood's avatar
Gavin Wood committed
		/// A remote has requested XCM version change notification from us and we have honored it.
		/// A version information message is sent to them and its cost is included.
Francisco Aguirre's avatar
Francisco Aguirre committed
		VersionNotifyStarted { destination: Location, cost: Assets, message_id: XcmHash },
		/// We have requested that a remote chain send us XCM version change notifications.
Francisco Aguirre's avatar
Francisco Aguirre committed
		VersionNotifyRequested { destination: Location, cost: Assets, message_id: XcmHash },
		/// We have requested that a remote chain stops sending us XCM version change
		/// notifications.
Francisco Aguirre's avatar
Francisco Aguirre committed
		VersionNotifyUnrequested { destination: Location, cost: Assets, message_id: XcmHash },
Gavin Wood's avatar
Gavin Wood committed
		/// Fees were paid from a location for an operation (often for using `SendXcm`).
Francisco Aguirre's avatar
Francisco Aguirre committed
		FeesPaid { paying: Location, fees: Assets },
		/// Some assets have been claimed from an asset trap
Francisco Aguirre's avatar
Francisco Aguirre committed
		AssetsClaimed { hash: H256, origin: Location, assets: VersionedAssets },
		/// A XCM version migration finished.
		VersionMigrationFinished { version: XcmVersion },
	#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
	pub enum Origin {
		/// It comes from somewhere in the XCM space wanting to transact.
Francisco Aguirre's avatar
Francisco Aguirre committed
		Xcm(Location),
		/// It comes as an expected response from an XCM location.
Francisco Aguirre's avatar
Francisco Aguirre committed
		Response(Location),
Francisco Aguirre's avatar
Francisco Aguirre committed
	impl From<Location> for Origin {
		fn from(location: Location) -> Origin {
Gavin Wood's avatar
Gavin Wood committed
	}

	#[pallet::error]
	pub enum Error<T> {
		/// The desired destination was unreachable, generally because there is a no way of routing
		/// to it.
Gavin Wood's avatar
Gavin Wood committed
		Unreachable,
		/// There was some other issue (i.e. not to do with routing) in sending the message.
		/// Perhaps a lack of space for buffering the message.
Gavin Wood's avatar
Gavin Wood committed
		SendFailure,
		/// The message execution fails the filter.
		Filtered,
		/// The message's weight could not be determined.
		UnweighableMessage,
Francisco Aguirre's avatar
Francisco Aguirre committed
		/// The destination `Location` provided cannot be inverted.
		DestinationNotInvertible,
		/// The assets to be sent are empty.
		Empty,
		/// Could not re-anchor the assets to declare the fees for the destination chain.
		CannotReanchor,
		/// Too many assets have been attempted for transfer.
		TooManyAssets,
		/// Origin is invalid for sending.
		InvalidOrigin,
		/// The version of the `Versioned` value used is not able to be interpreted.
		BadVersion,
		/// The given location could not be used (e.g. because it cannot be expressed in the
		/// desired version of XCM).
		BadLocation,
		/// The referenced subscription could not be found.
		NoSubscription,
		/// The location is invalid since it already has a subscription from us.
		AlreadySubscribed,
		/// Could not check-out the assets for teleportation to the destination chain.
		CannotCheckOutTeleport,
Gavin Wood's avatar
Gavin Wood committed
		/// The owner does not own (all) of the asset that they wish to do the operation on.
		LowBalance,
		/// The asset owner has too many locks on the asset.
		TooManyLocks,
		/// The given account is not an identifiable sovereign account for any location.
		AccountNotSovereign,
		/// The operation required fees to be paid which the initiator could not meet.
		FeesNotMet,
		/// A remote lock with the corresponding data could not be found.
		LockNotFound,
		/// The unlock operation cannot succeed because there are still consumers of the lock.
Gavin Wood's avatar
Gavin Wood committed
		InUse,
		/// Invalid asset, reserve chain could not be determined for it.
		#[codec(index = 21)]
		InvalidAssetUnknownReserve,
		/// Invalid asset, do not support remote asset reserves with different fees reserves.
		#[codec(index = 22)]
		InvalidAssetUnsupportedReserve,
		/// Too many assets with different reserve locations have been attempted for transfer.
		#[codec(index = 23)]
		#[codec(index = 24)]
Gavin Wood's avatar
Gavin Wood committed
	}

	impl<T: Config> From<SendError> for Error<T> {
		fn from(e: SendError) -> Self {
			match e {
				SendError::Fees => Error::<T>::FeesNotMet,
				SendError::NotApplicable => Error::<T>::Unreachable,
				_ => Error::<T>::SendFailure,
			}
		}
	impl<T: Config> From<AssetTransferError> for Error<T> {
		fn from(e: AssetTransferError) -> Self {
			match e {
				AssetTransferError::UnknownReserve => Error::<T>::InvalidAssetUnknownReserve,
			}
		}
	}

	#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
	pub enum QueryStatus<BlockNumber> {
		/// The query was sent but no response has yet been received.
		Pending {
Gavin Wood's avatar
Gavin Wood committed
			/// The `QueryResponse` XCM must have this origin to be considered a reply for this
			/// query.
Francisco Aguirre's avatar
Francisco Aguirre committed
			responder: VersionedLocation,
Gavin Wood's avatar
Gavin Wood committed
			/// The `QueryResponse` XCM must have this value as the `querier` field to be
			/// considered a reply for this query. If `None` then the querier is ignored.
Francisco Aguirre's avatar
Francisco Aguirre committed
			maybe_match_querier: Option<VersionedLocation>,
			maybe_notify: Option<(u8, u8)>,
			timeout: BlockNumber,
		},
		/// The query is for an ongoing version notification subscription.
Francisco Aguirre's avatar
Francisco Aguirre committed
		VersionNotifier { origin: VersionedLocation, is_active: bool },
		/// A response has been received.
		Ready { response: VersionedResponse, at: BlockNumber },
	}

	#[derive(Copy, Clone)]
Francisco Aguirre's avatar
Francisco Aguirre committed
	pub(crate) struct LatestVersionedLocation<'a>(pub(crate) &'a Location);
	impl<'a> EncodeLike<VersionedLocation> for LatestVersionedLocation<'a> {}
	impl<'a> Encode for LatestVersionedLocation<'a> {
		fn encode(&self) -> Vec<u8> {
Francisco Aguirre's avatar
Francisco Aguirre committed
			let mut r = VersionedLocation::from(Location::default()).encode();
			r.truncate(1);
			self.0.using_encoded(|d| r.extend_from_slice(d));
			r
		}
	}

	#[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo)]
	pub enum VersionMigrationStage {
		MigrateSupportedVersion,
		MigrateVersionNotifiers,
		NotifyCurrentTargets(Option<Vec<u8>>),
		MigrateAndNotifyOldTargets,
	}

	impl Default for VersionMigrationStage {
		fn default() -> Self {
			Self::MigrateSupportedVersion
		}
	}

	/// The latest available query index.
	#[pallet::storage]
	pub(super) type QueryCounter<T: Config> = StorageValue<_, QueryId, ValueQuery>;

	/// The ongoing queries.
	#[pallet::storage]
	#[pallet::getter(fn query)]
	pub(super) type Queries<T: Config> =
		StorageMap<_, Blake2_128Concat, QueryId, QueryStatus<BlockNumberFor<T>>, OptionQuery>;
	/// The existing asset traps.
	///
Francisco Aguirre's avatar
Francisco Aguirre committed
	/// Key is the blake2 256 hash of (origin, versioned `Assets`) pair. Value is the number of
	/// times this pair has been trapped (usually just 1 if it exists at all).
	#[pallet::storage]
	#[pallet::getter(fn asset_trap)]
	pub(super) type AssetTraps<T: Config> = StorageMap<_, Identity, H256, u32, ValueQuery>;

	/// Default version to encode XCM when latest version of destination is unknown. If `None`,
	/// then the destinations whose XCM version is unknown are considered unreachable.
	#[pallet::storage]
	#[pallet::whitelist_storage]
	pub(super) type SafeXcmVersion<T: Config> = StorageValue<_, XcmVersion, OptionQuery>;

	/// The Latest versions that we know various locations support.
	#[pallet::storage]
	pub(super) type SupportedVersion<T: Config> = StorageDoubleMap<
		_,
		Twox64Concat,
		XcmVersion,
		Blake2_128Concat,
Francisco Aguirre's avatar
Francisco Aguirre committed
		VersionedLocation,
		XcmVersion,
		OptionQuery,
	>;

	/// All locations that we have requested version notifications from.
	#[pallet::storage]
	pub(super) type VersionNotifiers<T: Config> = StorageDoubleMap<
		_,
		Twox64Concat,
		XcmVersion,
		Blake2_128Concat,
Francisco Aguirre's avatar
Francisco Aguirre committed
		VersionedLocation,
		QueryId,
		OptionQuery,
	>;

	/// The target locations that are subscribed to our version changes, as well as the most recent
	/// of our versions we informed them of.
	#[pallet::storage]
	pub(super) type VersionNotifyTargets<T: Config> = StorageDoubleMap<
		_,
		Twox64Concat,
		XcmVersion,
		Blake2_128Concat,
Francisco Aguirre's avatar
Francisco Aguirre committed
		VersionedLocation,
Gavin Wood's avatar
Gavin Wood committed
		(QueryId, Weight, XcmVersion),
		OptionQuery,
	>;

	pub struct VersionDiscoveryQueueSize<T>(PhantomData<T>);
	impl<T: Config> Get<u32> for VersionDiscoveryQueueSize<T> {
		fn get() -> u32 {
			T::VERSION_DISCOVERY_QUEUE_SIZE
		}
	}

	/// Destinations whose latest XCM version we would like to know. Duplicates not allowed, and
	/// the `u32` counter is the number of times that a send to the destination has been attempted,
	/// which is used as a prioritization.
	#[pallet::storage]
	#[pallet::whitelist_storage]
	pub(super) type VersionDiscoveryQueue<T: Config> = StorageValue<
		_,
Francisco Aguirre's avatar
Francisco Aguirre committed
		BoundedVec<(VersionedLocation, u32), VersionDiscoveryQueueSize<T>>,
		ValueQuery,
	>;

	/// The current migration's stage, if any.
	#[pallet::storage]
	pub(super) type CurrentMigration<T: Config> =
		StorageValue<_, VersionMigrationStage, OptionQuery>;

Gavin Wood's avatar
Gavin Wood committed
	#[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo, MaxEncodedLen)]
	#[scale_info(skip_type_params(MaxConsumers))]
	pub struct RemoteLockedFungibleRecord<ConsumerIdentifier, MaxConsumers: Get<u32>> {
		/// Total amount of the asset held by the remote lock.
Gavin Wood's avatar
Gavin Wood committed
		pub amount: u128,
		/// The owner of the locked asset.
Francisco Aguirre's avatar
Francisco Aguirre committed
		pub owner: VersionedLocation,
		/// The location which holds the original lock.
Francisco Aguirre's avatar
Francisco Aguirre committed
		pub locker: VersionedLocation,
		/// Local consumers of the remote lock with a consumer identifier and the amount
		/// of fungible asset every consumer holds.
		/// Every consumer can hold up to total amount of the remote lock.
		pub consumers: BoundedVec<(ConsumerIdentifier, u128), MaxConsumers>,
	}

	impl<LockId, MaxConsumers: Get<u32>> RemoteLockedFungibleRecord<LockId, MaxConsumers> {
		/// Amount of the remote lock in use by consumers.
		/// Returns `None` if the remote lock has no consumers.
		pub fn amount_held(&self) -> Option<u128> {
			self.consumers.iter().max_by(|x, y| x.1.cmp(&y.1)).map(|max| max.1)
		}
Gavin Wood's avatar
Gavin Wood committed
	}

	/// Fungible assets which we know are locked on a remote chain.
	#[pallet::storage]
	pub(super) type RemoteLockedFungibles<T: Config> = StorageNMap<
		_,
		(
			NMapKey<Twox64Concat, XcmVersion>,
			NMapKey<Blake2_128Concat, T::AccountId>,
			NMapKey<Blake2_128Concat, VersionedAssetId>,
		),
		RemoteLockedFungibleRecord<T::RemoteLockConsumerIdentifier, T::MaxRemoteLockConsumers>,
Gavin Wood's avatar
Gavin Wood committed
		OptionQuery,
	>;

	/// Fungible assets which we know are locked on this chain.
	#[pallet::storage]
	pub(super) type LockedFungibles<T: Config> = StorageMap<
		_,
		Blake2_128Concat,
		T::AccountId,
Francisco Aguirre's avatar
Francisco Aguirre committed
		BoundedVec<(BalanceOf<T>, VersionedLocation), T::MaxLockers>,
Gavin Wood's avatar
Gavin Wood committed
		OptionQuery,
	>;

	/// Global suspension state of the XCM executor.
	#[pallet::storage]
	pub(super) type XcmExecutionSuspended<T: Config> = StorageValue<_, bool, ValueQuery>;

	#[pallet::genesis_config]
	pub struct GenesisConfig<T: Config> {
		#[serde(skip)]
		pub _config: sp_std::marker::PhantomData<T>,
		/// The default version to encode outgoing XCM messages with.
		pub safe_xcm_version: Option<XcmVersion>,
	}

	impl<T: Config> Default for GenesisConfig<T> {
		fn default() -> Self {
			Self { safe_xcm_version: Some(XCM_VERSION), _config: Default::default() }
		}
	}

	#[pallet::genesis_build]
	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
		fn build(&self) {
			SafeXcmVersion::<T>::set(self.safe_xcm_version);
		}
	}

Gavin Wood's avatar
Gavin Wood committed
	#[pallet::hooks]
	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
		fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
			let mut weight_used = Weight::zero();
			if let Some(migration) = CurrentMigration::<T>::get() {
				// Consume 10% of block at most
				let max_weight = T::BlockWeights::get().max_block / 10;
				let (w, maybe_migration) = Self::check_xcm_version_change(migration, max_weight);
				if maybe_migration.is_none() {
					Self::deposit_event(Event::VersionMigrationFinished { version: XCM_VERSION });
				}
				CurrentMigration::<T>::set(maybe_migration);
				weight_used.saturating_accrue(w);
			}

			// Here we aim to get one successful version negotiation request sent per block, ordered
			// by the destinations being most sent to.
			let mut q = VersionDiscoveryQueue::<T>::take().into_inner();
			// TODO: correct weights.
			weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
			q.sort_by_key(|i| i.1);
			while let Some((versioned_dest, _)) = q.pop() {
Francisco Aguirre's avatar
Francisco Aguirre committed
				if let Ok(dest) = Location::try_from(versioned_dest) {
					if Self::request_version_notify(dest).is_ok() {
						// TODO: correct weights.
						weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
						break
					}
				}
			}
			// Should never fail since we only removed items. But better safe than panicking as it's
			// way better to drop the queue than panic on initialize.
			if let Ok(q) = BoundedVec::try_from(q) {
				VersionDiscoveryQueue::<T>::put(q);
			}
			weight_used
		}

		#[cfg(feature = "try-runtime")]
		fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
			Self::do_try_state()
		}
Gavin Wood's avatar
Gavin Wood committed
	pub mod migrations {
		use super::*;
		use frame_support::traits::{PalletInfoAccess, StorageVersion};

		#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
		enum QueryStatusV0<BlockNumber> {
			Pending {
Francisco Aguirre's avatar
Francisco Aguirre committed
				responder: VersionedLocation,
Gavin Wood's avatar
Gavin Wood committed
				maybe_notify: Option<(u8, u8)>,
				timeout: BlockNumber,
			},
			VersionNotifier {
Francisco Aguirre's avatar
Francisco Aguirre committed
				origin: VersionedLocation,
Gavin Wood's avatar
Gavin Wood committed
				is_active: bool,
			},
			Ready {
				response: VersionedResponse,
				at: BlockNumber,
			},
		}
		impl<B> From<QueryStatusV0<B>> for QueryStatus<B> {
			fn from(old: QueryStatusV0<B>) -> Self {
				use QueryStatusV0::*;
				match old {
					Pending { responder, maybe_notify, timeout } => QueryStatus::Pending {
						responder,
						maybe_notify,
						timeout,
Francisco Aguirre's avatar
Francisco Aguirre committed
						maybe_match_querier: Some(Location::here().into()),
Gavin Wood's avatar
Gavin Wood committed
					},
					VersionNotifier { origin, is_active } =>
						QueryStatus::VersionNotifier { origin, is_active },
					Ready { response, at } => QueryStatus::Ready { response, at },
				}
			}
		}

		pub fn migrate_to_v1<T: Config, P: GetStorageVersion + PalletInfoAccess>(
		) -> frame_support::weights::Weight {
			let on_chain_storage_version = <P as GetStorageVersion>::on_chain_storage_version();
			log::info!(
				target: "runtime::xcm",
				"Running migration storage v1 for xcm with storage version {:?}",
				on_chain_storage_version,
			);

			if on_chain_storage_version < 1 {
				let mut count = 0;
				Queries::<T>::translate::<QueryStatusV0<BlockNumberFor<T>>, _>(|_key, value| {
Gavin Wood's avatar
Gavin Wood committed
					count += 1;
					Some(value.into())
				});
				StorageVersion::new(1).put::<P>();
				log::info!(
					target: "runtime::xcm",
					"Running migration storage v1 for xcm with storage version {:?} was complete",
					on_chain_storage_version,
				);
				// calculate and return migration weights
				T::DbWeight::get().reads_writes(count as u64 + 1, count as u64 + 1)
			} else {
				log::warn!(
					target: "runtime::xcm",
					"Attempted to apply migration to v1 but failed because storage version is {:?}",
					on_chain_storage_version,
				);
				T::DbWeight::get().reads(1)
			}
		}
	}

	#[pallet::call(weight(<T as Config>::WeightInfo))]
Gavin Wood's avatar
Gavin Wood committed
	impl<T: Config> Pallet<T> {
		#[pallet::call_index(0)]
Francisco Aguirre's avatar
Francisco Aguirre committed
			dest: Box<VersionedLocation>,
			message: Box<VersionedXcm<()>>,
			<Self as SendController<_>>::send(origin, dest, message)?;
		/// Teleport some assets from the local chain to some destination chain.
		///
		/// **This function is deprecated: Use `limited_teleport_assets` instead.**
		///
		/// Fee payment on the destination side is made from the asset in the `assets` vector of
		/// index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,
		/// with all fees taken as needed from the asset.
		/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
Francisco Aguirre's avatar
Francisco Aguirre committed
		/// - `dest`: Destination context for the assets. Will typically be `[Parent,
		///   Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from
		///   relay to parachain.
		/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will
		///   generally be an `AccountId32` value.
		/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the
		///   fee on the `dest` chain.
		/// - `fee_asset_item`: The index into `assets` of the item which should be used to pay
		///   fees.
		#[pallet::call_index(1)]
		#[allow(deprecated)]
		#[deprecated(
			note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_teleport_assets` or `transfer_assets`"
		)]
		pub fn teleport_assets(
			origin: OriginFor<T>,
Francisco Aguirre's avatar
Francisco Aguirre committed
			dest: Box<VersionedLocation>,
			beneficiary: Box<VersionedLocation>,
			assets: Box<VersionedAssets>,
Gavin Wood's avatar
Gavin Wood committed
			fee_asset_item: u32,
		) -> DispatchResult {
			Self::do_teleport_assets(origin, dest, beneficiary, assets, fee_asset_item, Unlimited)
		/// Transfer some assets from the local chain to the destination chain through their local,
		/// destination or remote reserve.
		///
		/// `assets` must have same reserve location and may not be teleportable to `dest`.
		///  - `assets` have local reserve: transfer assets to sovereign account of destination
		///    chain and forward a notification XCM to `dest` to mint and deposit reserve-based
		///    assets to `beneficiary`.
		///  - `assets` have destination reserve: burn local assets and forward a notification to
		///    `dest` chain to withdraw the reserve assets from this chain's sovereign account and
		///    deposit them to `beneficiary`.
		///  - `assets` have remote reserve: burn local assets, forward XCM to reserve chain to move
		///    reserves from this chain's SA to `dest` chain's SA, and forward another XCM to `dest`
		///    to mint and deposit reserve-based assets to `beneficiary`.
		/// **This function is deprecated: Use `limited_reserve_transfer_assets` instead.**
		///
		/// Fee payment on the destination side is made from the asset in the `assets` vector of
		/// index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,
		/// with all fees taken as needed from the asset.
		/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
Francisco Aguirre's avatar
Francisco Aguirre committed
		/// - `dest`: Destination context for the assets. Will typically be `[Parent,
		///   Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from
		///   relay to parachain.
		/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will
		///   generally be an `AccountId32` value.
		/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the
		///   fee on the `dest` (and possibly reserve) chains.
		/// - `fee_asset_item`: The index into `assets` of the item which should be used to pay
		///   fees.
		#[pallet::call_index(2)]
		#[allow(deprecated)]
		#[deprecated(
			note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_reserve_transfer_assets` or `transfer_assets`"
		)]
		pub fn reserve_transfer_assets(
			origin: OriginFor<T>,
Francisco Aguirre's avatar
Francisco Aguirre committed
			dest: Box<VersionedLocation>,
			beneficiary: Box<VersionedLocation>,
			assets: Box<VersionedAssets>,
Gavin Wood's avatar
Gavin Wood committed
			fee_asset_item: u32,
		) -> DispatchResult {
			Self::do_reserve_transfer_assets(
				origin,
				dest,
				beneficiary,
				assets,
				fee_asset_item,
				Unlimited,
Gavin Wood's avatar
Gavin Wood committed
		/// Execute an XCM message from a local, signed, origin.