feat: initialize Kurdistan SDK - independent fork of Polkadot SDK
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user