feat: initialize Kurdistan SDK - independent fork of Polkadot SDK

This commit is contained in:
2025-12-13 15:44:15 +03:00
commit e4778b4576
6838 changed files with 1847450 additions and 0 deletions
@@ -0,0 +1,89 @@
// 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 <http://www.gnu.org/licenses/>.
use crate::AssetsInHolding;
use xcm::prelude::*;
/// A service for exchanging assets.
pub trait AssetExchange {
/// Handler for exchanging an asset.
///
/// - `origin`: The location attempting the exchange; this should generally not matter.
/// - `give`: The assets which have been removed from the caller.
/// - `want`: The minimum amount of assets which should be given to the caller in case any
/// exchange happens. If more assets are provided, then they should generally be of the same
/// asset class if at all possible.
/// - `maximal`: If `true`, then as much as possible should be exchanged.
///
/// `Ok` is returned along with the new set of assets which have been exchanged for `give`. At
/// least want must be in the set. Some assets originally in `give` may also be in this set. In
/// the case of returning an `Err`, then `give` is returned.
fn exchange_asset(
origin: Option<&Location>,
give: AssetsInHolding,
want: &Assets,
maximal: bool,
) -> Result<AssetsInHolding, AssetsInHolding>;
/// Handler for quoting the exchange price of two asset collections.
///
/// It's useful before calling `exchange_asset`, to get some information on whether or not the
/// exchange will be successful.
///
/// Arguments:
/// - `give` The asset(s) that are going to be given.
/// - `want` The asset(s) that are wanted.
/// - `maximal`:
/// - If `true`, then the return value is the resulting amount of `want` obtained by swapping
/// `give`.
/// - If `false`, then the return value is the required amount of `give` needed to get `want`.
///
/// The return value is `Assets` since it comprises both which assets and how much of them.
///
/// The relationship between this function and `exchange_asset` is the following:
/// - quote(give, want, maximal) = resulting_want -> exchange(give, resulting_want, maximal) ✅
/// - quote(give, want, minimal) = required_give -> exchange(required_give_amount, want,
/// minimal) ✅
fn quote_exchange_price(_give: &Assets, _want: &Assets, _maximal: bool) -> Option<Assets>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl AssetExchange for Tuple {
fn exchange_asset(
origin: Option<&Location>,
give: AssetsInHolding,
want: &Assets,
maximal: bool,
) -> Result<AssetsInHolding, AssetsInHolding> {
for_tuples!( #(
let give = match Tuple::exchange_asset(origin, give, want, maximal) {
Ok(r) => return Ok(r),
Err(a) => a,
};
)* );
Err(give)
}
fn quote_exchange_price(give: &Assets, want: &Assets, maximal: bool) -> Option<Assets> {
for_tuples!( #(
match Tuple::quote_exchange_price(give, want, maximal) {
Some(assets) => return Some(assets),
None => {}
}
)* );
None
}
}
@@ -0,0 +1,140 @@
// 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 <http://www.gnu.org/licenses/>.
use core::convert::Infallible;
use xcm::prelude::*;
#[derive(Debug)]
pub enum LockError {
NotApplicable,
WouldClobber,
BadOrigin,
NotLocked,
NotEnoughLocked,
Unimplemented,
NotTrusted,
BadOwner,
UnknownAsset,
AssetNotOwned,
NoResources,
UnexpectedState,
InUse,
}
impl From<LockError> for XcmError {
fn from(e: LockError) -> XcmError {
use LockError::*;
match e {
NotApplicable => XcmError::AssetNotFound,
BadOrigin => XcmError::BadOrigin,
WouldClobber | NotLocked | NotEnoughLocked | Unimplemented | NotTrusted |
BadOwner | UnknownAsset | AssetNotOwned | NoResources | UnexpectedState | InUse =>
XcmError::LockError,
}
}
}
pub trait Enact {
/// Enact a lock. This should generally be infallible if called immediately after being
/// received.
fn enact(self) -> Result<(), LockError>;
}
impl Enact for Infallible {
fn enact(self) -> Result<(), LockError> {
unreachable!()
}
}
/// Define a handler for notification of an asset being locked and for the unlock instruction.
pub trait AssetLock {
/// `Enact` implementer for `prepare_lock`. This type may be dropped safely to avoid doing the
/// lock.
type LockTicket: Enact;
/// `Enact` implementer for `prepare_unlock`. This type may be dropped safely to avoid doing the
/// unlock.
type UnlockTicket: Enact;
/// `Enact` implementer for `prepare_reduce_unlockable`. This type may be dropped safely to
/// avoid doing the unlock.
type ReduceTicket: Enact;
/// Prepare to lock an asset. On success, a `Self::LockTicket` it returned, which can be used
/// to actually enact the lock.
///
/// WARNING: Don't call this with an undropped instance of `Self::LockTicket` or
/// `Self::UnlockTicket`.
fn prepare_lock(
unlocker: Location,
asset: Asset,
owner: Location,
) -> Result<Self::LockTicket, LockError>;
/// Prepare to unlock an asset. On success, a `Self::UnlockTicket` it returned, which can be
/// used to actually enact the lock.
///
/// WARNING: Don't call this with an undropped instance of `Self::LockTicket` or
/// `Self::UnlockTicket`.
fn prepare_unlock(
locker: Location,
asset: Asset,
owner: Location,
) -> Result<Self::UnlockTicket, LockError>;
/// Handler for when a location reports to us that an asset has been locked for us to unlock
/// at a later stage.
///
/// If there is no way to handle the lock report, then this should return an error so that the
/// sending chain can ensure the lock does not remain.
///
/// We should only act upon this message if we believe that the `origin` is honest.
fn note_unlockable(locker: Location, asset: Asset, owner: Location) -> Result<(), LockError>;
/// Handler for when an owner wishes to unlock an asset on a remote chain.
///
/// Returns a ticket which can be used to actually note the reduction in unlockable assets that
/// `owner` commands on `locker`.
///
/// WARNING: Don't call this with an undropped instance of `Self::ReduceTicket`.
fn prepare_reduce_unlockable(
locker: Location,
asset: Asset,
owner: Location,
) -> Result<Self::ReduceTicket, LockError>;
}
impl AssetLock for () {
type LockTicket = Infallible;
type UnlockTicket = Infallible;
type ReduceTicket = Infallible;
fn prepare_lock(_: Location, _: Asset, _: Location) -> Result<Self::LockTicket, LockError> {
Err(LockError::NotApplicable)
}
fn prepare_unlock(_: Location, _: Asset, _: Location) -> Result<Self::UnlockTicket, LockError> {
Err(LockError::NotApplicable)
}
fn note_unlockable(_: Location, _: Asset, _: Location) -> Result<(), LockError> {
Err(LockError::NotApplicable)
}
fn prepare_reduce_unlockable(
_: Location,
_: Asset,
_: Location,
) -> Result<Self::ReduceTicket, LockError> {
Err(LockError::NotApplicable)
}
}
@@ -0,0 +1,94 @@
// 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 <http://www.gnu.org/licenses/>.
use crate::traits::TransactAsset;
use frame_support::traits::ContainsPair;
use scale_info::TypeInfo;
use sp_runtime::codec::{Decode, DecodeWithMemTracking, Encode};
use xcm::prelude::*;
/// Errors related to determining asset transfer support.
#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
pub enum Error {
/// Reserve chain could not be determined for assets.
UnknownReserve,
}
/// Specify which type of asset transfer is required for a particular `(asset, dest)` combination.
#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Debug, TypeInfo)]
pub enum TransferType {
/// should teleport `asset` to `dest`
Teleport,
/// should reserve-transfer `asset` to `dest`, using local chain as reserve
LocalReserve,
/// should reserve-transfer `asset` to `dest`, using `dest` as reserve
DestinationReserve,
/// should reserve-transfer `asset` to `dest`, using remote chain `Location` as reserve
RemoteReserve(VersionedLocation),
}
/// A trait for identifying asset transfer type based on `IsTeleporter` and `IsReserve`
/// configurations.
pub trait XcmAssetTransfers {
/// Combinations of (Asset, Location) pairs which we trust as reserves. Meaning
/// reserve-based-transfers are to be used for assets matching this filter.
type IsReserve: ContainsPair<Asset, Location>;
/// Combinations of (Asset, Location) pairs which we trust as teleporters. Meaning teleports are
/// to be used for assets matching this filter.
type IsTeleporter: ContainsPair<Asset, Location>;
/// How to withdraw and deposit an asset.
type AssetTransactor: TransactAsset;
/// Determine transfer type to be used for transferring `asset` from local chain to `dest`.
fn determine_for(asset: &Asset, dest: &Location) -> Result<TransferType, Error> {
if Self::IsTeleporter::contains(asset, dest) {
// we trust destination for teleporting asset
return Ok(TransferType::Teleport);
} else if Self::IsReserve::contains(asset, dest) {
// we trust destination as asset reserve location
return Ok(TransferType::DestinationReserve);
}
// try to determine reserve location based on asset id/location
let asset_location = asset.id.0.chain_location();
if asset_location == Location::here() ||
Self::IsTeleporter::contains(asset, &asset_location)
{
// if the asset is local, then it's a local reserve
// it's also a local reserve if the asset's location is not `here` but it's a location
// where it can be teleported to `here` => local reserve
Ok(TransferType::LocalReserve)
} else if Self::IsReserve::contains(asset, &asset_location) {
// remote location that is recognized as reserve location for asset
Ok(TransferType::RemoteReserve(asset_location.into()))
} else {
// remote location that is not configured either as teleporter or reserve => cannot
// determine asset reserve
Err(Error::UnknownReserve)
}
}
}
impl XcmAssetTransfers for () {
type IsReserve = ();
type IsTeleporter = ();
type AssetTransactor = ();
fn determine_for(_: &Asset, _: &Location) -> Result<TransferType, Error> {
return Err(Error::UnknownReserve);
}
}
@@ -0,0 +1,173 @@
// 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 <http://www.gnu.org/licenses/>.
use core::{marker::PhantomData, result::Result};
use frame_support::traits::{Contains, OriginTrait};
use sp_runtime::{traits::Dispatchable, DispatchErrorWithPostInfo};
use xcm::latest::prelude::*;
/// Means of converting a location into an account identifier.
pub trait ConvertLocation<AccountId> {
/// Convert the `location` into `Some` account ID, or `None` if not possible.
fn convert_location(location: &Location) -> Option<AccountId>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<AccountId> ConvertLocation<AccountId> for Tuple {
fn convert_location(l: &Location) -> Option<AccountId> {
for_tuples!( #(
match Tuple::convert_location(l) {
Some(result) => return Some(result),
None => {},
}
)* );
None
}
}
/// A converter `trait` for origin types.
///
/// Can be amalgamated into tuples. If any of the tuple elements returns `Ok(_)`, it short circuits.
/// Else, the `Err(_)` of the last tuple item is returned. Each intermediate `Err(_)` might return a
/// different `origin` of type `Origin` which is passed to the next convert item.
///
/// ```rust
/// # use xcm::latest::{Location, Junctions, Junction, OriginKind};
/// # use staging_xcm_executor::traits::ConvertOrigin;
/// // A convertor that will bump the para id and pass it to the next one.
/// struct BumpParaId;
/// impl ConvertOrigin<u32> for BumpParaId {
/// fn convert_origin(origin: impl Into<Location>, _: OriginKind) -> Result<u32, Location> {
/// match origin.into().unpack() {
/// (0, [Junction::Teyrchain(id)]) => {
/// Err([Junction::Teyrchain(id + 1)].into())
/// }
/// _ => unreachable!()
/// }
/// }
/// }
///
/// struct AcceptPara7;
/// impl ConvertOrigin<u32> for AcceptPara7 {
/// fn convert_origin(origin: impl Into<Location>, _: OriginKind) -> Result<u32, Location> {
/// let origin = origin.into();
/// match origin.unpack() {
/// (0, [Junction::Teyrchain(id)]) if *id == 7 => {
/// Ok(7)
/// }
/// _ => Err(origin)
/// }
/// }
/// }
/// # fn main() {
/// let origin: Location = [Junction::Teyrchain(6)].into();
/// assert!(
/// <(BumpParaId, AcceptPara7) as ConvertOrigin<u32>>::convert_origin(origin, OriginKind::Native)
/// .is_ok()
/// );
/// # }
/// ```
pub trait ConvertOrigin<Origin> {
/// Attempt to convert `origin` to the generic `Origin` whilst consuming it.
fn convert_origin(origin: impl Into<Location>, kind: OriginKind) -> Result<Origin, Location>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<O> ConvertOrigin<O> for Tuple {
fn convert_origin(origin: impl Into<Location>, kind: OriginKind) -> Result<O, Location> {
let origin = origin.into();
tracing::trace!(
target: "xcm::convert_origin",
?origin,
?kind,
"Converting origin",
);
for_tuples!( #(
let convert_origin = core::any::type_name::<Tuple>();
let origin = match Tuple::convert_origin(origin, kind) {
Err(o) => {
tracing::trace!(
target: "xcm::convert_origin",
%convert_origin,
"Convert origin step failed",
);
o
},
Ok(o) => {
tracing::trace!(
target: "xcm::convert_origin",
%convert_origin,
"Convert origin step succeeded",
);
return Ok(o)
}
};
)* );
tracing::trace!(
target: "xcm::convert_origin",
"Converting origin failed",
);
Err(origin)
}
}
/// Defines how a call is dispatched with given origin.
/// Allows to customize call dispatch, such as adapting the origin based on the call
/// or modifying the call.
pub trait CallDispatcher<Call: Dispatchable> {
fn dispatch(
call: Call,
origin: Call::RuntimeOrigin,
) -> Result<Call::PostInfo, DispatchErrorWithPostInfo<Call::PostInfo>>;
}
pub struct WithOriginFilter<Filter>(PhantomData<Filter>);
impl<Call, Filter> CallDispatcher<Call> for WithOriginFilter<Filter>
where
Call: Dispatchable,
Call::RuntimeOrigin: OriginTrait,
<<Call as Dispatchable>::RuntimeOrigin as OriginTrait>::Call: 'static,
Filter: Contains<<<Call as Dispatchable>::RuntimeOrigin as OriginTrait>::Call> + 'static,
{
fn dispatch(
call: Call,
mut origin: <Call as Dispatchable>::RuntimeOrigin,
) -> Result<
<Call as Dispatchable>::PostInfo,
DispatchErrorWithPostInfo<<Call as Dispatchable>::PostInfo>,
> {
origin.add_filter(Filter::contains);
call.dispatch(origin)
}
}
// We implement it for every calls so they can dispatch themselves
// (without any change).
impl<Call: Dispatchable> CallDispatcher<Call> for Call {
fn dispatch(
call: Call,
origin: Call::RuntimeOrigin,
) -> Result<Call::PostInfo, DispatchErrorWithPostInfo<Call::PostInfo>> {
call.dispatch(origin)
}
}
@@ -0,0 +1,91 @@
// 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 <http://www.gnu.org/licenses/>.
use crate::AssetsInHolding;
use core::marker::PhantomData;
use frame_support::traits::Contains;
use xcm::latest::{Assets, Location, Weight, XcmContext};
/// Define a handler for when some non-empty `AssetsInHolding` value should be dropped.
pub trait DropAssets {
/// Handler for receiving dropped assets. Returns the weight consumed by this operation.
fn drop_assets(origin: &Location, assets: AssetsInHolding, context: &XcmContext) -> Weight;
}
impl DropAssets for () {
fn drop_assets(_origin: &Location, _assets: AssetsInHolding, _context: &XcmContext) -> Weight {
Weight::zero()
}
}
/// Morph a given `DropAssets` implementation into one which can filter based on assets. This can
/// be used to ensure that `AssetsInHolding` values which hold no value are ignored.
#[allow(dead_code)]
pub struct FilterAssets<D, A>(PhantomData<(D, A)>);
impl<D: DropAssets, A: Contains<AssetsInHolding>> DropAssets for FilterAssets<D, A> {
fn drop_assets(origin: &Location, assets: AssetsInHolding, context: &XcmContext) -> Weight {
if A::contains(&assets) {
D::drop_assets(origin, assets, context)
} else {
Weight::zero()
}
}
}
/// Morph a given `DropAssets` implementation into one which can filter based on origin. This can
/// be used to ban origins which don't have proper protections/policies against misuse of the
/// asset trap facility don't get to use it.
#[allow(dead_code)]
pub struct FilterOrigin<D, O>(PhantomData<(D, O)>);
impl<D: DropAssets, O: Contains<Location>> DropAssets for FilterOrigin<D, O> {
fn drop_assets(origin: &Location, assets: AssetsInHolding, context: &XcmContext) -> Weight {
if O::contains(origin) {
D::drop_assets(origin, assets, context)
} else {
Weight::zero()
}
}
}
/// Define any handlers for the `AssetClaim` instruction.
pub trait ClaimAssets {
/// Claim any assets available to `origin` and return them in a single `Assets` value, together
/// with the weight used by this operation.
fn claim_assets(
origin: &Location,
ticket: &Location,
what: &Assets,
context: &XcmContext,
) -> bool;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl ClaimAssets for Tuple {
fn claim_assets(
origin: &Location,
ticket: &Location,
what: &Assets,
context: &XcmContext,
) -> bool {
for_tuples!( #(
if Tuple::claim_assets(origin, ticket, what, context) {
return true;
}
)* );
false
}
}
@@ -0,0 +1,84 @@
// 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 <http://www.gnu.org/licenses/>.
use xcm::{
latest::{Location, SendError, Xcm, XcmHash},
prelude::XcmError,
};
/// Defines the event emitter for the XCM executor.
/// This trait allows implementations to emit events related to XCM handling, including successful
/// sends and failure cases.
pub trait EventEmitter {
/// Emits an event when an XCM is successfully sent.
///
/// # Parameters
/// - `origin`: The origin location of the XCM.
/// - `destination`: The target location where the message is sent.
/// - `message`: `Some(Xcm)` for `pallet_xcm::Event::Sent`, `None` for other events to reduce
/// storage.
/// - `message_id`: A unique identifier for the XCM.
fn emit_sent_event(
origin: Location,
destination: Location,
message: Option<Xcm<()>>,
message_id: XcmHash,
);
/// Emits an event when an XCM fails to send.
///
/// # Parameters
/// - `origin`: The origin location of the XCM.
/// - `destination`: The intended target location.
/// - `error`: The error encountered while sending.
/// - `message_id`: The unique identifier for the failed message.
fn emit_send_failure_event(
origin: Location,
destination: Location,
error: SendError,
message_id: XcmHash,
);
/// Emits an event when an XCM fails to process.
///
/// # Parameters
/// - `origin`: The origin location of the message.
/// - `error`: The error encountered while processing.
/// - `message_id`: The unique identifier for the failed message.
fn emit_process_failure_event(origin: Location, error: XcmError, message_id: XcmHash);
}
/// A no-op implementation of `EventEmitter` for unit type `()`.
/// This can be used when event emission is not required.
impl EventEmitter for () {
fn emit_sent_event(
_origin: Location,
_destination: Location,
_message: Option<Xcm<()>>,
_message_id: XcmHash,
) {
}
fn emit_send_failure_event(
_origin: Location,
_destination: Location,
_error: SendError,
_message_id: XcmHash,
) {
}
fn emit_process_failure_event(_origin: Location, _error: XcmError, _message_id: XcmHash) {}
}
@@ -0,0 +1,146 @@
// 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 <http://www.gnu.org/licenses/>.
use xcm::latest::prelude::*;
/// Utility for delivering a message to a system under a different (non-local) consensus with a
/// spoofed origin. This essentially defines the behaviour of the `ExportMessage` XCM instruction.
///
/// This is quite different to `SendXcm`; `SendXcm` assumes that the local side's location will be
/// preserved to be represented as the value of the Origin register during the message's execution.
///
/// This trait on the other hand assumes that we do not necessarily want the Origin register to
/// contain the local (i.e. the caller chain's) location, since it will generally be exporting a
/// message on behalf of another consensus system. Therefore in addition to the message, the
/// destination must be given in two parts: the network and the interior location within it.
///
/// We also require the caller to state exactly what location they purport to be representing. The
/// destination must accept the local location to represent that location or the operation will
/// fail.
pub trait ExportXcm {
/// Intermediate value which connects the two phases of the export operation.
type Ticket;
/// Check whether the given `message` is deliverable to the given `destination` on `network`,
/// spoofing its source as `universal_source` and if so determine the cost which will be paid by
/// this chain to do so, returning a `Ticket` token which can be used to enact delivery.
///
/// The `channel` to be used on the `network`'s export mechanism (bridge, probably) must also
/// be provided.
///
/// The `destination` and `message` must be `Some` (or else an error will be returned) and they
/// may only be consumed if the `Err` is not `NotApplicable`.
///
/// If it is not a destination that can be reached with this type, but possibly could be with
/// others, then this *MUST* return `NotApplicable`. Any other error will cause the tuple
/// implementation (used to compose routing systems from different delivery agents) to exit
/// early without trying alternative means of delivery.
fn validate(
network: NetworkId,
channel: u32,
universal_source: &mut Option<InteriorLocation>,
destination: &mut Option<InteriorLocation>,
message: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket>;
/// Actually carry out the delivery operation for a previously validated message sending.
///
/// The implementation should do everything possible to ensure that this function is infallible
/// if called immediately after `validate`. Returning an error here would result in a price
/// paid without the service being delivered.
fn deliver(ticket: Self::Ticket) -> Result<XcmHash, SendError>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl ExportXcm for Tuple {
for_tuples! { type Ticket = (#( Option<Tuple::Ticket> ),* ); }
fn validate(
network: NetworkId,
channel: u32,
universal_source: &mut Option<InteriorLocation>,
destination: &mut Option<InteriorLocation>,
message: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket> {
let mut maybe_cost: Option<Assets> = None;
let one_ticket: Self::Ticket = (for_tuples! { #(
if maybe_cost.is_some() {
None
} else {
match Tuple::validate(network, channel, universal_source, destination, message) {
Err(SendError::NotApplicable) => None,
Err(e) => { return Err(e) },
Ok((v, c)) => {
maybe_cost = Some(c);
Some(v)
},
}
}
),* });
if let Some(cost) = maybe_cost {
Ok((one_ticket, cost))
} else {
Err(SendError::NotApplicable)
}
}
fn deliver(mut one_ticket: Self::Ticket) -> Result<XcmHash, SendError> {
for_tuples!( #(
if let Some(validated) = one_ticket.Tuple.take() {
return Tuple::deliver(validated);
}
)* );
Err(SendError::Unroutable)
}
}
/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps
/// both in `Some` before passing them as mutable references into `T::send_xcm`.
pub fn validate_export<T: ExportXcm>(
network: NetworkId,
channel: u32,
universal_source: InteriorLocation,
dest: InteriorLocation,
msg: Xcm<()>,
) -> SendResult<T::Ticket> {
T::validate(network, channel, &mut Some(universal_source), &mut Some(dest), &mut Some(msg))
}
/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps
/// both in `Some` before passing them as mutable references into `T::send_xcm`.
///
/// Returns either `Ok` with the price of the delivery, or `Err` with the reason why the message
/// could not be sent.
///
/// Generally you'll want to validate and get the price first to ensure that the sender can pay it
/// before actually doing the delivery.
pub fn export_xcm<T: ExportXcm>(
network: NetworkId,
channel: u32,
universal_source: InteriorLocation,
dest: InteriorLocation,
msg: Xcm<()>,
) -> Result<(XcmHash, Assets), SendError> {
let (ticket, price) = T::validate(
network,
channel,
&mut Some(universal_source),
&mut Some(dest),
&mut Some(msg),
)?;
let hash = T::deliver(ticket)?;
Ok((hash, price))
}
@@ -0,0 +1,72 @@
// 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 <http://www.gnu.org/licenses/>.
use xcm::prelude::*;
/// Handle stuff to do with taking fees in certain XCM instructions.
pub trait FeeManager {
/// Determine if a fee should be waived.
fn is_waived(origin: Option<&Location>, r: FeeReason) -> bool;
/// Do something with the fee which has been paid. Doing nothing here silently burns the
/// fees.
fn handle_fee(fee: Assets, context: Option<&XcmContext>, r: FeeReason);
}
/// Context under which a fee is paid.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum FeeReason {
/// When a reporting instruction is called.
Report,
/// When the `TransferReserveAsset` instruction is called.
TransferReserveAsset,
/// When the `DepositReserveAsset` instruction is called.
DepositReserveAsset,
/// When the `InitiateReserveWithdraw` instruction is called.
InitiateReserveWithdraw,
/// When the `InitiateTeleport` instruction is called.
InitiateTeleport,
/// When the `InitiateTransfer` instruction is called.
InitiateTransfer,
/// When the `QueryPallet` instruction is called.
QueryPallet,
/// When the `ExportMessage` instruction is called (and includes the network ID).
Export { network: NetworkId, destination: InteriorLocation },
/// The `charge_fees` API.
ChargeFees,
/// When the `LockAsset` instruction is called.
LockAsset,
/// When the `RequestUnlock` instruction is called.
RequestUnlock,
}
impl FeeManager for () {
fn is_waived(_: Option<&Location>, _: FeeReason) -> bool {
false
}
fn handle_fee(_: Assets, _: Option<&XcmContext>, _: FeeReason) {}
}
pub struct WaiveDeliveryFees;
impl FeeManager for WaiveDeliveryFees {
fn is_waived(_: Option<&Location>, _: FeeReason) -> bool {
true
}
fn handle_fee(_: Assets, _: Option<&XcmContext>, _: FeeReason) {}
}
@@ -0,0 +1,35 @@
// 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 <http://www.gnu.org/licenses/>.
use frame_support::traits::ContainsPair;
use xcm::latest::{Asset, Location};
/// Filters assets/location pairs.
///
/// Can be amalgamated into tuples. If any item returns `true`, it short-circuits, else `false` is
/// returned.
#[deprecated = "Use `frame_support::traits::ContainsPair<Asset, Location>` instead"]
pub trait FilterAssetLocation {
/// A filter to distinguish between asset/location pairs.
fn contains(asset: &Asset, origin: &Location) -> bool;
}
#[allow(deprecated)]
impl<T: ContainsPair<Asset, Location>> FilterAssetLocation for T {
fn contains(asset: &Asset, origin: &Location) -> bool {
T::contains(asset, origin)
}
}
@@ -0,0 +1,56 @@
// 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 <http://www.gnu.org/licenses/>.
use xcm::latest::Result as XcmResult;
/// Executes logic when a `HrmpNewChannelOpenRequest` XCM notification is received.
pub trait HandleHrmpNewChannelOpenRequest {
fn handle(sender: u32, max_message_size: u32, max_capacity: u32) -> XcmResult;
}
/// Executes optional logic when a `HrmpChannelAccepted` XCM notification is received.
pub trait HandleHrmpChannelAccepted {
fn handle(recipient: u32) -> XcmResult;
}
/// Executes optional logic when a `HrmpChannelClosing` XCM notification is received.
pub trait HandleHrmpChannelClosing {
fn handle(initiator: u32, sender: u32, recipient: u32) -> XcmResult;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl HandleHrmpNewChannelOpenRequest for Tuple {
fn handle(sender: u32, max_message_size: u32, max_capacity: u32) -> XcmResult {
for_tuples!( #( Tuple::handle(sender, max_message_size, max_capacity)?; )* );
Ok(())
}
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl HandleHrmpChannelAccepted for Tuple {
fn handle(recipient: u32) -> XcmResult {
for_tuples!( #( Tuple::handle(recipient)?; )* );
Ok(())
}
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl HandleHrmpChannelClosing for Tuple {
fn handle(initiator: u32, sender: u32, recipient: u32) -> XcmResult {
for_tuples!( #( Tuple::handle(initiator, sender, recipient)?; )* );
Ok(())
}
}
@@ -0,0 +1,73 @@
// 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 <http://www.gnu.org/licenses/>.
//! Various traits used in configuring the executor.
mod conversion;
pub use conversion::{CallDispatcher, ConvertLocation, ConvertOrigin, WithOriginFilter};
mod drop_assets;
pub use drop_assets::{ClaimAssets, DropAssets};
mod asset_exchange;
pub use asset_exchange::AssetExchange;
mod asset_lock;
pub use asset_lock::{AssetLock, Enact, LockError};
mod asset_transfer;
pub use asset_transfer::{Error as AssetTransferError, TransferType, XcmAssetTransfers};
mod export;
pub use export::{export_xcm, validate_export, ExportXcm};
mod fee_manager;
pub use fee_manager::{FeeManager, FeeReason, WaiveDeliveryFees};
mod filter_asset_location;
#[allow(deprecated)]
pub use filter_asset_location::FilterAssetLocation;
mod token_matching;
pub use token_matching::{
Error, MatchesFungible, MatchesFungibles, MatchesInstance, MatchesNonFungible,
MatchesNonFungibles,
};
mod on_response;
pub use on_response::{OnResponse, QueryHandler, QueryResponseStatus, VersionChangeNotifier};
mod process_transaction;
pub use process_transaction::ProcessTransaction;
mod should_execute;
pub use should_execute::{CheckSuspension, DenyExecution, Properties, ShouldExecute};
mod transact_asset;
pub use transact_asset::TransactAsset;
mod hrmp;
pub use hrmp::{
HandleHrmpChannelAccepted, HandleHrmpChannelClosing, HandleHrmpNewChannelOpenRequest,
};
mod event_emitter;
mod record_xcm;
mod weight;
pub use event_emitter::EventEmitter;
pub use record_xcm::RecordXcm;
#[deprecated = "Use `sp_runtime::traits::` instead"]
pub use sp_runtime::traits::{Identity, TryConvertInto as JustTry};
pub use weight::{WeightBounds, WeightTrader};
pub mod prelude {
pub use super::{
export_xcm, validate_export, AssetExchange, AssetLock, ClaimAssets, ConvertOrigin,
DropAssets, Enact, Error, EventEmitter, ExportXcm, FeeManager, FeeReason, LockError,
MatchesFungible, MatchesFungibles, MatchesInstance, MatchesNonFungible,
MatchesNonFungibles, OnResponse, ProcessTransaction, ShouldExecute, TransactAsset,
VersionChangeNotifier, WeightBounds, WeightTrader, WithOriginFilter,
};
#[allow(deprecated)]
pub use super::{Identity, JustTry};
}
@@ -0,0 +1,181 @@
// 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 <http://www.gnu.org/licenses/>.
use crate::{Junctions::Here, Xcm};
use codec::{Decode, Encode};
use core::{fmt::Debug, result};
use frame_support::{pallet_prelude::Get, parameter_types};
use sp_arithmetic::traits::Zero;
use xcm::latest::{
Error as XcmError, InteriorLocation, Location, QueryId, Response, Result as XcmResult, Weight,
XcmContext,
};
/// Define what needs to be done upon receiving a query response.
pub trait OnResponse {
/// Returns `true` if we are expecting a response from `origin` for query `query_id` that was
/// queried by `querier`.
fn expecting_response(origin: &Location, query_id: u64, querier: Option<&Location>) -> bool;
/// Handler for receiving a `response` from `origin` relating to `query_id` initiated by
/// `querier`.
fn on_response(
origin: &Location,
query_id: u64,
querier: Option<&Location>,
response: Response,
max_weight: Weight,
context: &XcmContext,
) -> Weight;
}
impl OnResponse for () {
fn expecting_response(_origin: &Location, _query_id: u64, _querier: Option<&Location>) -> bool {
false
}
fn on_response(
_origin: &Location,
_query_id: u64,
_querier: Option<&Location>,
_response: Response,
_max_weight: Weight,
_context: &XcmContext,
) -> Weight {
Weight::zero()
}
}
/// Trait for a type which handles notifying a destination of XCM version changes.
pub trait VersionChangeNotifier {
/// Start notifying `location` should the XCM version of this chain change.
///
/// When it does, this type should ensure a `QueryResponse` message is sent with the given
/// `query_id` & `max_weight` and with a `response` of `Response::Version`. This should happen
/// until/unless `stop` is called with the correct `query_id`.
///
/// If the `location` has an ongoing notification and when this function is called, then an
/// error should be returned.
fn start(
location: &Location,
query_id: QueryId,
max_weight: Weight,
context: &XcmContext,
) -> XcmResult;
/// Stop notifying `location` should the XCM change. Returns an error if there is no existing
/// notification set up.
fn stop(location: &Location, context: &XcmContext) -> XcmResult;
/// Return true if a location is subscribed to XCM version changes.
fn is_subscribed(location: &Location) -> bool;
}
impl VersionChangeNotifier for () {
fn start(_: &Location, _: QueryId, _: Weight, _: &XcmContext) -> XcmResult {
Err(XcmError::Unimplemented)
}
fn stop(_: &Location, _: &XcmContext) -> XcmResult {
Err(XcmError::Unimplemented)
}
fn is_subscribed(_: &Location) -> bool {
false
}
}
/// The possible state of an XCM query response.
#[derive(Debug, PartialEq, Eq, Encode, Decode)]
pub enum QueryResponseStatus<BlockNumber> {
/// The response has arrived, and includes the inner Response and the block number it arrived
/// at.
Ready { response: Response, at: BlockNumber },
/// The response has not yet arrived, the XCM might still be executing or the response might be
/// in transit.
Pending { timeout: BlockNumber },
/// No response with the given `QueryId` was found, or the response was already queried and
/// removed from local storage.
NotFound,
/// Got an unexpected XCM version.
UnexpectedVersion,
}
/// Provides methods to expect responses from XCMs and query their status.
pub trait QueryHandler {
type BlockNumber: Zero + Encode;
type Error;
type UniversalLocation: Get<InteriorLocation>;
/// Attempt to create a new query ID and register it as a query that is yet to respond.
fn new_query(
responder: impl Into<Location>,
timeout: Self::BlockNumber,
match_querier: impl Into<Location>,
) -> QueryId;
/// Consume `message` and return another which is equivalent to it except that it reports
/// back the outcome.
///
/// - `message`: The message whose outcome should be reported.
/// - `responder`: The origin from which a response should be expected.
/// - `timeout`: The block number after which it is permissible to return `NotFound` from
/// `take_response`.
///
/// `report_outcome` may return an error if the `responder` is not invertible.
///
/// It is assumed that the querier of the response will be `Here`.
/// The response can be queried with `take_response`.
fn report_outcome(
message: &mut Xcm<()>,
responder: impl Into<Location>,
timeout: Self::BlockNumber,
) -> result::Result<QueryId, Self::Error>;
/// Attempt to remove and return the response of query with ID `query_id`.
fn take_response(id: QueryId) -> QueryResponseStatus<Self::BlockNumber>;
/// Makes sure to expect a response with the given id.
#[cfg(feature = "runtime-benchmarks")]
fn expect_response(id: QueryId, response: Response);
}
parameter_types! {
pub UniversalLocation: InteriorLocation = Here;
}
impl QueryHandler for () {
type BlockNumber = u64;
type Error = ();
type UniversalLocation = UniversalLocation;
fn take_response(_query_id: QueryId) -> QueryResponseStatus<Self::BlockNumber> {
QueryResponseStatus::NotFound
}
fn new_query(
_responder: impl Into<Location>,
_timeout: Self::BlockNumber,
_match_querier: impl Into<Location>,
) -> QueryId {
0u64
}
fn report_outcome(
_message: &mut Xcm<()>,
_responder: impl Into<Location>,
_timeout: Self::BlockNumber,
) -> Result<QueryId, Self::Error> {
Err(())
}
#[cfg(feature = "runtime-benchmarks")]
fn expect_response(_id: QueryId, _response: crate::Response) {}
}
@@ -0,0 +1,57 @@
// 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 <http://www.gnu.org/licenses/>.
use xcm::latest::prelude::*;
/// Provides mechanisms for transactional processing of XCM instructions.
///
/// This trait defines the behavior required to process XCM instructions in a transactional
/// manner. Implementers of this trait can ensure that XCM instructions are executed
/// atomically, meaning they either fully succeed or fully fail without any partial effects.
///
/// Implementers of this trait can also choose to not process XCM instructions transactionally.
/// This is useful for cases where the implementer is not able to provide transactional guarantees.
/// In this case the `IS_TRANSACTIONAL` constant should be set to `false`.
/// The `()` type implements this trait in a non-transactional manner.
pub trait ProcessTransaction {
/// Whether or not the implementor of the this trait is actually transactional.
const IS_TRANSACTIONAL: bool;
/// Processes an XCM instruction encapsulated within the provided closure. Responsible for
/// processing an XCM instruction transactionally. If the closure returns an error, any
/// changes made during its execution should be rolled back. In the case where the
/// implementer is not able to provide transactional guarantees, the closure should be
/// executed as is.
/// # Parameters
/// - `f`: A closure that encapsulates the XCM instruction being processed. It will return a
/// `Result` indicating the success or failure of the instruction.
///
/// # Returns
/// - A `Result` indicating the overall success or failure of the transactional process.
fn process<F>(f: F) -> Result<(), XcmError>
where
F: FnOnce() -> Result<(), XcmError>;
}
impl ProcessTransaction for () {
const IS_TRANSACTIONAL: bool = false;
fn process<F>(f: F) -> Result<(), XcmError>
where
F: FnOnce() -> Result<(), XcmError>,
{
f()
}
}
@@ -0,0 +1,46 @@
// 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 <http://www.gnu.org/licenses/>.
//! Trait for recording XCMs and a dummy implementation.
use xcm::latest::Xcm;
/// Trait for recording XCMs.
pub trait RecordXcm {
/// Whether or not we should record incoming XCMs.
fn should_record() -> bool;
/// Enable or disable recording.
fn set_record_xcm(enabled: bool);
/// Get recorded XCM.
/// Returns `None` if no message was sent, or if recording was off.
fn recorded_xcm() -> Option<Xcm<()>>;
/// Record `xcm`.
fn record(xcm: Xcm<()>);
}
impl RecordXcm for () {
fn should_record() -> bool {
false
}
fn set_record_xcm(_: bool) {}
fn recorded_xcm() -> Option<Xcm<()>> {
None
}
fn record(_: Xcm<()>) {}
}
@@ -0,0 +1,193 @@
// 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 <http://www.gnu.org/licenses/>.
use core::result::Result;
use frame_support::traits::ProcessMessageError;
use xcm::latest::{Instruction, Location, Weight, XcmHash};
/// Properties of an XCM message and its imminent execution.
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct Properties {
/// The amount of weight that the system has determined this
/// message may utilize in its execution. Typically non-zero only because of prior fee
/// payment, but could in principle be due to other factors.
pub weight_credit: Weight,
/// The identity of the message, if one is known. If left as `None`, then it will generally
/// default to the hash of the message which may be non-unique.
pub message_id: Option<XcmHash>,
}
/// Trait to determine whether the execution engine should actually execute a given XCM.
///
/// Can be amalgamated into a tuple to have multiple trials. If any of the tuple elements returns
/// `Ok(())`, the execution stops. Else, `Err(_)` is returned if all elements reject the message.
pub trait ShouldExecute {
/// Returns `Ok(())` if the given `message` may be executed.
///
/// - `origin`: The origin (sender) of the message.
/// - `instructions`: The message itself.
/// - `max_weight`: The (possibly over-) estimation of the weight of execution of the message.
/// - `properties`: Various pre-established properties of the message which may be mutated by
/// this API.
fn should_execute<RuntimeCall>(
origin: &Location,
instructions: &mut [Instruction<RuntimeCall>],
max_weight: Weight,
properties: &mut Properties,
) -> Result<(), ProcessMessageError>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl ShouldExecute for Tuple {
fn should_execute<RuntimeCall>(
origin: &Location,
instructions: &mut [Instruction<RuntimeCall>],
max_weight: Weight,
properties: &mut Properties,
) -> Result<(), ProcessMessageError> {
for_tuples!( #(
let barrier = core::any::type_name::<Tuple>();
match Tuple::should_execute(origin, instructions, max_weight, properties) {
Ok(()) => {
tracing::trace!(
target: "xcm::should_execute",
?origin,
?instructions,
?max_weight,
?properties,
%barrier,
"pass barrier",
);
return Ok(())
},
Err(error) => {
tracing::trace!(
target: "xcm::should_execute",
?origin,
?instructions,
?max_weight,
?properties,
?error,
%barrier,
"did not pass barrier",
);
},
}
)* );
Err(ProcessMessageError::Unsupported)
}
}
/// Trait to determine whether the execution engine is suspended from executing a given XCM.
///
/// The trait method is given the same parameters as `ShouldExecute::should_execute`, so that the
/// implementer will have all the context necessary to determine whether or not to suspend the
/// XCM executor.
///
/// Can be chained together in tuples to have multiple rounds of checks. If all of the tuple
/// elements returns false, then execution is not suspended. Otherwise, execution is suspended
/// if any of the tuple elements returns true.
pub trait CheckSuspension {
fn is_suspended<Call>(
origin: &Location,
instructions: &mut [Instruction<Call>],
max_weight: Weight,
properties: &mut Properties,
) -> bool;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl CheckSuspension for Tuple {
fn is_suspended<Call>(
origin: &Location,
instruction: &mut [Instruction<Call>],
max_weight: Weight,
properties: &mut Properties,
) -> bool {
for_tuples!( #(
if Tuple::is_suspended(origin, instruction, max_weight, properties) {
return true
}
)* );
false
}
}
/// Trait to determine whether the execution engine should not execute a given XCM.
///
/// Can be amalgamated into a tuple to have multiple traits. If any of the tuple elements returns
/// `Err(ProcessMessageError)`, the execution stops. Else, `Ok(())` is returned if all elements
/// accept the message.
pub trait DenyExecution {
/// Returns `Ok(())` if there is no reason to deny execution,
/// while `Err(ProcessMessageError)` indicates there is a reason to deny execution.
///
/// - `origin`: The origin (sender) of the message.
/// - `instructions`: The message itself.
/// - `max_weight`: The (possibly over-) estimation of the weight of execution of the message.
/// - `properties`: Various pre-established properties of the message which may be mutated by
/// this API.
fn deny_execution<RuntimeCall>(
origin: &Location,
instructions: &mut [Instruction<RuntimeCall>],
max_weight: Weight,
properties: &mut Properties,
) -> Result<(), ProcessMessageError>;
}
#[impl_trait_for_tuples::impl_for_tuples(10)]
impl DenyExecution for Tuple {
fn deny_execution<RuntimeCall>(
origin: &Location,
instructions: &mut [Instruction<RuntimeCall>],
max_weight: Weight,
properties: &mut Properties,
) -> Result<(), ProcessMessageError> {
for_tuples!( #(
let barrier = core::any::type_name::<Tuple>();
match Tuple::deny_execution(origin, instructions, max_weight, properties) {
Err(error) => {
tracing::error!(
target: "xcm::deny_execution",
?origin,
?instructions,
?max_weight,
?properties,
?error,
%barrier,
"did not pass barrier",
);
return Err(error);
},
Ok(()) => {
tracing::trace!(
target: "xcm::deny_execution",
?origin,
?instructions,
?max_weight,
?properties,
%barrier,
"pass barrier",
);
},
}
)* );
Ok(())
}
}
@@ -0,0 +1,134 @@
// 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 <http://www.gnu.org/licenses/>.
use core::result;
use xcm::latest::prelude::*;
pub trait MatchesFungible<Balance> {
fn matches_fungible(a: &Asset) -> Option<Balance>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<Balance> MatchesFungible<Balance> for Tuple {
fn matches_fungible(a: &Asset) -> Option<Balance> {
for_tuples!( #(
match Tuple::matches_fungible(a) { o @ Some(_) => return o, _ => () }
)* );
tracing::trace!(target: "xcm::matches_fungible", asset = ?a, "did not match fungible asset");
None
}
}
pub trait MatchesNonFungible<Instance> {
fn matches_nonfungible(a: &Asset) -> Option<Instance>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<Instance> MatchesNonFungible<Instance> for Tuple {
fn matches_nonfungible(a: &Asset) -> Option<Instance> {
for_tuples!( #(
match Tuple::matches_nonfungible(a) { o @ Some(_) => return o, _ => () }
)* );
tracing::trace!(target: "xcm::matches_non_fungible", asset = ?a, "did not match non-fungible asset");
None
}
}
/// Errors associated with [`MatchesFungibles`] operation.
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
/// The given asset is not handled. (According to [`XcmError::AssetNotFound`])
AssetNotHandled,
/// `Location` to `AccountId` conversion failed.
AccountIdConversionFailed,
/// `u128` amount to currency `Balance` conversion failed.
AmountToBalanceConversionFailed,
/// `Location` to `AssetId`/`ClassId` conversion failed.
AssetIdConversionFailed,
/// `AssetInstance` to non-fungibles instance ID conversion failed.
InstanceConversionFailed,
}
impl From<Error> for XcmError {
fn from(e: Error) -> Self {
use XcmError::FailedToTransactAsset;
match e {
Error::AssetNotHandled => XcmError::AssetNotFound,
Error::AccountIdConversionFailed => FailedToTransactAsset("AccountIdConversionFailed"),
Error::AmountToBalanceConversionFailed =>
FailedToTransactAsset("AmountToBalanceConversionFailed"),
Error::AssetIdConversionFailed => FailedToTransactAsset("AssetIdConversionFailed"),
Error::InstanceConversionFailed => FailedToTransactAsset("InstanceConversionFailed"),
}
}
}
pub trait MatchesFungibles<AssetId, Balance> {
fn matches_fungibles(a: &Asset) -> result::Result<(AssetId, Balance), Error>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<AssetId, Balance> MatchesFungibles<AssetId, Balance> for Tuple {
fn matches_fungibles(a: &Asset) -> result::Result<(AssetId, Balance), Error> {
for_tuples!( #(
match Tuple::matches_fungibles(a) { o @ Ok(_) => return o, _ => () }
)* );
tracing::trace!(target: "xcm::matches_fungibles", asset = ?a, "did not match fungibles asset");
Err(Error::AssetNotHandled)
}
}
pub trait MatchesNonFungibles<AssetId, Instance> {
fn matches_nonfungibles(a: &Asset) -> result::Result<(AssetId, Instance), Error>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<AssetId, Instance> MatchesNonFungibles<AssetId, Instance> for Tuple {
fn matches_nonfungibles(a: &Asset) -> result::Result<(AssetId, Instance), Error> {
for_tuples!( #(
match Tuple::matches_nonfungibles(a) { o @ Ok(_) => return o, _ => () }
)* );
tracing::trace!(target: "xcm::matches_non_fungibles", asset = ?a, "did not match non-fungibles asset");
Err(Error::AssetNotHandled)
}
}
/// Unique instances matcher trait.
///
/// The `Id` type should be defined in such a way so that its value can unambigiously identify an
/// instance. I.e., if instances are grouped (e.g., as tokens in an NFT collection), the `Id` should
/// contain both the group ID and the item group-local ID.
///
/// This unified interface allows us to avoid duplicating the XCM adapters for non-grouped and
/// grouped instances.
///
/// NOTE: The trait implementors should follow the convention of identifying the collection-less
/// NFTs by an XCM `Asset` of the form `{ asset_id: NFT_ID, fun:
/// Fungibility::NonFungible(AssetInstance::Undefined) }`.
pub trait MatchesInstance<Id> {
fn matches_instance(a: &Asset) -> result::Result<Id, Error>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<Id> MatchesInstance<Id> for Tuple {
fn matches_instance(a: &Asset) -> result::Result<Id, Error> {
for_tuples!( #(
match Tuple::matches_instance(a) { o @ Ok(_) => return o, _ => () }
)* );
tracing::trace!(target: "xcm::matches_instance", asset = ?a, "did not match an asset instance");
Err(Error::AssetNotHandled)
}
}
@@ -0,0 +1,551 @@
// 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 <http://www.gnu.org/licenses/>.
use crate::{AssetsInHolding, Weight};
use core::result::Result;
use xcm::latest::{Asset, Error as XcmError, Location, Result as XcmResult, XcmContext};
/// Facility for asset transacting.
///
/// This should work with as many asset/location combinations as possible. Locations to support may
/// include non-account locations such as a `[Junction::Teyrchain]`. Different
/// chains may handle them in different ways.
///
/// Can be amalgamated as a tuple of items that implement this trait. In such executions, if any of
/// the transactors returns `Ok(())`, then it will short circuit. Else, execution is passed to the
/// next transactor.
pub trait TransactAsset {
/// Ensure that `check_in` will do as expected.
///
/// When composed as a tuple, all type-items are called and at least one must result in `Ok`.
fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
Err(XcmError::Unimplemented)
}
/// An asset has been teleported in from the given origin. This should do whatever housekeeping
/// is needed.
///
/// NOTE: This will make only a best-effort at bookkeeping. The caller should ensure that
/// `can_check_in` has returned with `Ok` in order to guarantee that this operation proceeds
/// properly.
///
/// Implementation note: In general this will do one of two things: On chains where the asset is
/// native, it will reduce the assets from a special "teleported" account so that a)
/// total-issuance is preserved; and b) to ensure that no more assets can be teleported in than
/// were teleported out overall (this should not be needed if the teleporting chains are to be
/// trusted, but better to be safe than sorry). On chains where the asset is not native then it
/// will generally just be a no-op.
///
/// When composed as a tuple, all type-items are called. It is up to the implementer that there
/// exists no value for `_what` which can cause side-effects for more than one of the
/// type-items.
fn check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) {}
/// Ensure that `check_out` will do as expected.
///
/// When composed as a tuple, all type-items are called and at least one must result in `Ok`.
fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
Err(XcmError::Unimplemented)
}
/// An asset has been teleported out to the given destination. This should do whatever
/// housekeeping is needed.
///
/// Implementation note: In general this will do one of two things: On chains where the asset is
/// native, it will increase the assets in a special "teleported" account so that a)
/// total-issuance is preserved; and b) to ensure that no more assets can be teleported in than
/// were teleported out overall (this should not be needed if the teleporting chains are to be
/// trusted, but better to be safe than sorry). On chains where the asset is not native then it
/// will generally just be a no-op.
///
/// When composed as a tuple, all type-items are called. It is up to the implementer that there
/// exists no value for `_what` which can cause side-effects for more than one of the
/// type-items.
fn check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) {}
/// Deposit the `what` asset into the account of `who`.
///
/// Implementations should return `XcmError::FailedToTransactAsset` if deposit failed.
fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult {
Err(XcmError::Unimplemented)
}
/// Identical to `deposit_asset` but returning the surplus, if any.
///
/// Return the difference between the worst-case weight and the actual weight consumed.
/// This can be zero most of the time unless there's some metering involved.
fn deposit_asset_with_surplus(
what: &Asset,
who: &Location,
context: Option<&XcmContext>,
) -> Result<Weight, XcmError> {
Self::deposit_asset(what, who, context).map(|()| Weight::zero())
}
/// Withdraw the given asset from the consensus system.
///
/// Return the actual asset(s) withdrawn, which should always be equal to `_what`.
///
/// The XCM `_maybe_context` parameter may be `None` when the caller of `withdraw_asset` is
/// outside of the context of a currently-executing XCM. An example will be the `charge_fees`
/// method in the XCM executor.
///
/// Implementations should return `XcmError::FailedToTransactAsset` if withdraw failed.
fn withdraw_asset(
_what: &Asset,
_who: &Location,
_maybe_context: Option<&XcmContext>,
) -> Result<AssetsInHolding, XcmError> {
Err(XcmError::Unimplemented)
}
/// Withdraw assets returning surplus.
///
/// The surplus is the difference between the worst-case weight and the actual weight consumed.
/// This can be zero most of the time unless there's some metering involved.
fn withdraw_asset_with_surplus(
what: &Asset,
who: &Location,
maybe_context: Option<&XcmContext>,
) -> Result<(AssetsInHolding, Weight), XcmError> {
Self::withdraw_asset(what, who, maybe_context).map(|assets| (assets, Weight::zero()))
}
/// Move an `asset` `from` one location in `to` another location.
///
/// Returns `XcmError::FailedToTransactAsset` if transfer failed.
///
/// ## Notes
/// This function is meant to only be implemented by the type implementing `TransactAsset`, and
/// not be called directly. Most common API usages will instead call `transfer_asset`, which in
/// turn has a default implementation that calls `internal_transfer_asset`. As such, **please
/// do not call this method directly unless you know what you're doing**.
fn internal_transfer_asset(
_asset: &Asset,
_from: &Location,
_to: &Location,
_context: &XcmContext,
) -> Result<AssetsInHolding, XcmError> {
Err(XcmError::Unimplemented)
}
/// Identical to `internal_transfer_asset` but returning the surplus, if any.
///
/// The surplus is the difference between the worst-case weight and the actual
/// consumed weight.
/// This can be zero usually if there's no metering involved.
fn internal_transfer_asset_with_surplus(
asset: &Asset,
from: &Location,
to: &Location,
context: &XcmContext,
) -> Result<(AssetsInHolding, Weight), XcmError> {
Self::internal_transfer_asset(asset, from, to, context)
.map(|assets| (assets, Weight::zero()))
}
/// Move an `asset` `from` one location in `to` another location.
///
/// Attempts to use `internal_transfer_asset` and if not available then falls back to using a
/// two-part withdraw/deposit.
fn transfer_asset(
asset: &Asset,
from: &Location,
to: &Location,
context: &XcmContext,
) -> Result<AssetsInHolding, XcmError> {
match Self::internal_transfer_asset(asset, from, to, context) {
Err(XcmError::AssetNotFound | XcmError::Unimplemented) => {
let assets = Self::withdraw_asset(asset, from, Some(context))?;
Self::deposit_asset(asset, to, Some(context))?;
Ok(assets)
},
result => result,
}
}
/// Identical to `transfer_asset` but returning the surplus, if any.
///
/// The surplus is the difference between the worst-case weight and the actual
/// consumed weight.
/// This can be zero usually if there's no metering involved.
fn transfer_asset_with_surplus(
asset: &Asset,
from: &Location,
to: &Location,
context: &XcmContext,
) -> Result<(AssetsInHolding, Weight), XcmError> {
match Self::internal_transfer_asset_with_surplus(asset, from, to, context) {
Err(XcmError::AssetNotFound | XcmError::Unimplemented) => {
let (assets, withdraw_surplus) =
Self::withdraw_asset_with_surplus(asset, from, Some(context))?;
let deposit_surplus = Self::deposit_asset_with_surplus(asset, to, Some(context))?;
let total_surplus = withdraw_surplus.saturating_add(deposit_surplus);
Ok((assets, total_surplus))
},
result => result,
}
}
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl TransactAsset for Tuple {
fn can_check_in(origin: &Location, what: &Asset, context: &XcmContext) -> XcmResult {
for_tuples!( #(
match Tuple::can_check_in(origin, what, context) {
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
tracing::trace!(
target: "xcm::TransactAsset::can_check_in",
?what,
?origin,
?context,
"asset not found",
);
Err(XcmError::AssetNotFound)
}
fn check_in(origin: &Location, what: &Asset, context: &XcmContext) {
for_tuples!( #(
Tuple::check_in(origin, what, context);
)* );
}
fn can_check_out(dest: &Location, what: &Asset, context: &XcmContext) -> XcmResult {
for_tuples!( #(
match Tuple::can_check_out(dest, what, context) {
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
tracing::trace!(
target: "xcm::TransactAsset::can_check_out",
?what,
?dest,
?context,
"asset not found",
);
Err(XcmError::AssetNotFound)
}
fn check_out(dest: &Location, what: &Asset, context: &XcmContext) {
for_tuples!( #(
Tuple::check_out(dest, what, context);
)* );
}
fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult {
for_tuples!( #(
match Tuple::deposit_asset(what, who, context) {
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
tracing::trace!(
target: "xcm::TransactAsset::deposit_asset",
?what,
?who,
?context,
"did not deposit asset",
);
Err(XcmError::AssetNotFound)
}
fn deposit_asset_with_surplus(
what: &Asset,
who: &Location,
context: Option<&XcmContext>,
) -> Result<Weight, XcmError> {
for_tuples!( #(
match Tuple::deposit_asset_with_surplus(what, who, context) {
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
tracing::trace!(
target: "xcm::TransactAsset::deposit_asset",
?what,
?who,
?context,
"did not deposit asset",
);
Err(XcmError::AssetNotFound)
}
fn withdraw_asset(
what: &Asset,
who: &Location,
maybe_context: Option<&XcmContext>,
) -> Result<AssetsInHolding, XcmError> {
for_tuples!( #(
match Tuple::withdraw_asset(what, who, maybe_context) {
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
tracing::trace!(
target: "xcm::TransactAsset::withdraw_asset",
?what,
?who,
?maybe_context,
"did not withdraw asset",
);
Err(XcmError::AssetNotFound)
}
fn withdraw_asset_with_surplus(
what: &Asset,
who: &Location,
maybe_context: Option<&XcmContext>,
) -> Result<(AssetsInHolding, Weight), XcmError> {
for_tuples!( #(
match Tuple::withdraw_asset_with_surplus(what, who, maybe_context) {
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
tracing::trace!(
target: "xcm::TransactAsset::withdraw_asset",
?what,
?who,
?maybe_context,
"did not withdraw asset",
);
Err(XcmError::AssetNotFound)
}
fn internal_transfer_asset(
what: &Asset,
from: &Location,
to: &Location,
context: &XcmContext,
) -> Result<AssetsInHolding, XcmError> {
for_tuples!( #(
match Tuple::internal_transfer_asset(what, from, to, context) {
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
tracing::trace!(
target: "xcm::TransactAsset::internal_transfer_asset",
?what,
?from,
?to,
?context,
"did not transfer asset",
);
Err(XcmError::AssetNotFound)
}
fn internal_transfer_asset_with_surplus(
what: &Asset,
from: &Location,
to: &Location,
context: &XcmContext,
) -> Result<(AssetsInHolding, Weight), XcmError> {
for_tuples!( #(
match Tuple::internal_transfer_asset_with_surplus(what, from, to, context) {
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
tracing::trace!(
target: "xcm::TransactAsset::internal_transfer_asset",
?what,
?from,
?to,
?context,
"did not transfer asset",
);
Err(XcmError::AssetNotFound)
}
}
#[cfg(test)]
mod tests {
use super::*;
use xcm::latest::Junctions::Here;
pub struct UnimplementedTransactor;
impl TransactAsset for UnimplementedTransactor {}
pub struct NotFoundTransactor;
impl TransactAsset for NotFoundTransactor {
fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
Err(XcmError::AssetNotFound)
}
fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
Err(XcmError::AssetNotFound)
}
fn deposit_asset(
_what: &Asset,
_who: &Location,
_context: Option<&XcmContext>,
) -> XcmResult {
Err(XcmError::AssetNotFound)
}
fn withdraw_asset(
_what: &Asset,
_who: &Location,
_context: Option<&XcmContext>,
) -> Result<AssetsInHolding, XcmError> {
Err(XcmError::AssetNotFound)
}
fn internal_transfer_asset(
_what: &Asset,
_from: &Location,
_to: &Location,
_context: &XcmContext,
) -> Result<AssetsInHolding, XcmError> {
Err(XcmError::AssetNotFound)
}
}
pub struct OverflowTransactor;
impl TransactAsset for OverflowTransactor {
fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
Err(XcmError::Overflow)
}
fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
Err(XcmError::Overflow)
}
fn deposit_asset(
_what: &Asset,
_who: &Location,
_context: Option<&XcmContext>,
) -> XcmResult {
Err(XcmError::Overflow)
}
fn withdraw_asset(
_what: &Asset,
_who: &Location,
_context: Option<&XcmContext>,
) -> Result<AssetsInHolding, XcmError> {
Err(XcmError::Overflow)
}
fn internal_transfer_asset(
_what: &Asset,
_from: &Location,
_to: &Location,
_context: &XcmContext,
) -> Result<AssetsInHolding, XcmError> {
Err(XcmError::Overflow)
}
}
pub struct SuccessfulTransactor;
impl TransactAsset for SuccessfulTransactor {
fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
Ok(())
}
fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult {
Ok(())
}
fn deposit_asset(
_what: &Asset,
_who: &Location,
_context: Option<&XcmContext>,
) -> XcmResult {
Ok(())
}
fn withdraw_asset(
_what: &Asset,
_who: &Location,
_context: Option<&XcmContext>,
) -> Result<AssetsInHolding, XcmError> {
Ok(AssetsInHolding::default())
}
fn internal_transfer_asset(
_what: &Asset,
_from: &Location,
_to: &Location,
_context: &XcmContext,
) -> Result<AssetsInHolding, XcmError> {
Ok(AssetsInHolding::default())
}
}
#[test]
fn defaults_to_asset_not_found() {
type MultiTransactor =
(UnimplementedTransactor, NotFoundTransactor, UnimplementedTransactor);
assert_eq!(
MultiTransactor::deposit_asset(
&(Here, 1u128).into(),
&Here.into(),
Some(&XcmContext::with_message_id([0; 32])),
),
Err(XcmError::AssetNotFound)
);
}
#[test]
fn unimplemented_and_not_found_continue_iteration() {
type MultiTransactor = (UnimplementedTransactor, NotFoundTransactor, SuccessfulTransactor);
assert_eq!(
MultiTransactor::deposit_asset(
&(Here, 1u128).into(),
&Here.into(),
Some(&XcmContext::with_message_id([0; 32])),
),
Ok(())
);
}
#[test]
fn unexpected_error_stops_iteration() {
type MultiTransactor = (OverflowTransactor, SuccessfulTransactor);
assert_eq!(
MultiTransactor::deposit_asset(
&(Here, 1u128).into(),
&Here.into(),
Some(&XcmContext::with_message_id([0; 32])),
),
Err(XcmError::Overflow)
);
}
#[test]
fn success_stops_iteration() {
type MultiTransactor = (SuccessfulTransactor, OverflowTransactor);
assert_eq!(
MultiTransactor::deposit_asset(
&(Here, 1u128).into(),
&Here.into(),
Some(&XcmContext::with_message_id([0; 32])),
),
Ok(()),
);
}
}
@@ -0,0 +1,129 @@
// 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 <http://www.gnu.org/licenses/>.
use crate::AssetsInHolding;
use core::result::Result;
use xcm::latest::{prelude::*, Weight};
/// Determine the weight of an XCM message.
pub trait WeightBounds<RuntimeCall> {
/// Return the maximum amount of weight that an attempted execution of this message could
/// consume.
fn weight(
message: &mut Xcm<RuntimeCall>,
weight_limit: Weight,
) -> Result<Weight, InstructionError>;
/// Return the maximum amount of weight that an attempted execution of this instruction could
/// consume.
fn instr_weight(instruction: &mut Instruction<RuntimeCall>) -> Result<Weight, XcmError>;
}
/// Charge for weight in order to execute XCM.
///
/// A `WeightTrader` may also be put into a tuple, in which case the default behavior of
/// `buy_weight` and `refund_weight` would be to attempt to call each tuple element's own
/// implementation of these two functions, in the order of which they appear in the tuple,
/// returning early when a successful result is returned.
pub trait WeightTrader: Sized {
/// Create a new trader instance.
fn new() -> Self;
/// Purchase execution weight credit in return for up to a given `payment`. If less of the
/// payment is required then the surplus is returned. If the `payment` cannot be used to pay
/// for the `weight`, then an error is returned.
fn buy_weight(
&mut self,
weight: Weight,
payment: AssetsInHolding,
context: &XcmContext,
) -> Result<AssetsInHolding, XcmError>;
/// Attempt a refund of `weight` into some asset. The caller does not guarantee that the weight
/// was purchased using `buy_weight`.
///
/// Default implementation refunds nothing.
fn refund_weight(&mut self, _weight: Weight, _context: &XcmContext) -> Option<Asset> {
None
}
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl WeightTrader for Tuple {
fn new() -> Self {
for_tuples!( ( #( Tuple::new() ),* ) )
}
fn buy_weight(
&mut self,
weight: Weight,
payment: AssetsInHolding,
context: &XcmContext,
) -> Result<AssetsInHolding, XcmError> {
let mut too_expensive_error_found = false;
let mut last_error = None;
for_tuples!( #(
let weight_trader = core::any::type_name::<Tuple>();
match Tuple.buy_weight(weight, payment.clone(), context) {
Ok(assets) => {
tracing::trace!(
target: "xcm::buy_weight",
%weight_trader,
"Buy weight succeeded",
);
return Ok(assets)
},
Err(error) => {
if let XcmError::TooExpensive = error {
too_expensive_error_found = true;
}
last_error = Some(error);
tracing::trace!(
target: "xcm::buy_weight",
?error,
%weight_trader,
"Weight trader failed",
);
}
}
)* );
tracing::trace!(
target: "xcm::buy_weight",
"Buy weight failed",
);
// if we have multiple traders, and first one returns `TooExpensive` and others fail e.g.
// `AssetNotFound` then it is more accurate to return `TooExpensive` then `AssetNotFound`
Err(if too_expensive_error_found {
XcmError::TooExpensive
} else {
last_error.unwrap_or(XcmError::TooExpensive)
})
}
fn refund_weight(&mut self, weight: Weight, context: &XcmContext) -> Option<Asset> {
for_tuples!( #(
if let Some(asset) = Tuple.refund_weight(weight, context) {
return Some(asset);
}
)* );
None
}
}