* MultiAsset TWO

* Draft next MultiAsset API.

* XCM core builds

* XCM Executor builds

* XCM Builder builds

* API changes making their way throughout

* Some TODOs

* Further build fixes

* Basic compile builds

* First test fixed

* All executor tests fixed

* Typo

* Optimize subsume_assets and add test

* Optimize checked_sub

* XCM Builder first test fixed

* Fix builder tests

* Fix doc test

* fix some doc tests

* spelling

* named fields for AllOf

* Update xcm/src/v0/multiasset.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Update xcm/src/v0/multiasset.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Update xcm/src/v0/multiasset.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Update xcm/src/v0/multiasset.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Reformat

* Move to XCM version 1

* Spelling

* warnings

* Replace some more v0->v1s

* warnings

* format

* Add max_assets param

* building

* test fixes

* tests

* another test

* final test

* tests

* Rename Null -> Here

* Introduce

* More ergonomics

* More ergonomics

* test fix

* test fixes

* docs

* BuyExecution includes

* Fix XCM extrinsics

* fmt

* Make Vec<MultiAsset>/MultiAssets conversions safe

* More MultiAssets conversion safety

* spelling

* fix doc test

* Apply suggestions from code review

Co-authored-by: Amar Singh <asinghchrony@protonmail.com>

* Apply suggestions from code review

Co-authored-by: Amar Singh <asinghchrony@protonmail.com>

* fmt

* Add v0, remove VersionedMultiAsset

* Remove VersionedMultiLocation

* Update xcm/src/v1/order.rs

Co-authored-by: Amar Singh <asinghchrony@protonmail.com>

* Update xcm/src/v1/mod.rs

Co-authored-by: Amar Singh <asinghchrony@protonmail.com>

* XCM v0 backwards compatibility

* Full compatibility

* fmt

* Update xcm/pallet-xcm/src/lib.rs

* Update xcm/src/v0/order.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* Tweaks to versioning system

* Fixes

* fmt

* Update xcm/xcm-executor/src/assets.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* Update xcm/xcm-executor/src/assets.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* Grumbles

* Update xcm/src/v1/multiasset.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* fmt

* Update xcm/src/v1/multiasset.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* Update xcm/src/v1/multiasset.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* Fixes

* Formatting

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>
Co-authored-by: Amar Singh <asinghchrony@protonmail.com>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Gavin Wood
2021-08-06 18:25:01 +02:00
committed by GitHub
parent d86bb658a0
commit ce80bc2d4c
49 changed files with 3475 additions and 1242 deletions
+11 -10
View File
@@ -19,29 +19,30 @@
use parity_scale_codec::Encode;
use runtime_parachains::{configuration, dmp};
use sp_std::marker::PhantomData;
use xcm::opaque::{
v0::{Error, Junction, MultiLocation, Result, SendXcm, Xcm},
VersionedXcm,
};
use xcm::opaque::v1::{Error, Junction, MultiLocation, Result, SendXcm, Xcm};
/// XCM sender for relay chain. It only sends downward message.
pub struct ChildParachainRouter<T>(PhantomData<T>);
pub struct ChildParachainRouter<T, W>(PhantomData<(T, W)>);
impl<T: configuration::Config + dmp::Config> SendXcm for ChildParachainRouter<T> {
impl<T: configuration::Config + dmp::Config, W: xcm::WrapVersion> SendXcm
for ChildParachainRouter<T, W>
{
fn send_xcm(dest: MultiLocation, msg: Xcm) -> Result {
match dest {
match &dest {
MultiLocation::X1(Junction::Parachain(id)) => {
// Downward message passing.
let versioned_xcm =
W::wrap_version(&dest, msg).map_err(|()| Error::DestinationUnsupported)?;
let config = <configuration::Pallet<T>>::config();
<dmp::Pallet<T>>::queue_downward_message(
&config,
id.into(),
VersionedXcm::from(msg).encode(),
(*id).into(),
versioned_xcm.encode(),
)
.map_err(Into::<Error>::into)?;
Ok(())
},
d => Err(Error::CannotReachDestination(d, msg)),
_ => Err(Error::CannotReachDestination(dest, msg)),
}
}
}
+9 -66
View File
@@ -75,13 +75,7 @@ use sp_staking::SessionIndex;
use sp_version::NativeVersion;
use sp_version::RuntimeVersion;
use static_assertions::const_assert;
use xcm::v0::{
BodyId,
Junction::Parachain,
MultiAsset::{self, AllConcreteFungible},
MultiLocation::{self, Null, X1},
NetworkId, Xcm,
};
use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom,
BackingToPlurality, ChildParachainAsNative, ChildParachainConvertsVia,
@@ -1205,14 +1199,14 @@ impl auctions::Config for Runtime {
parameter_types! {
/// The location of the KSM token, from the context of this chain. Since this token is native to this
/// chain, we make it synonymous with it and thus it is the `Null` location, which means "equivalent to
/// chain, we make it synonymous with it and thus it is the `Here` location, which means "equivalent to
/// the context".
pub const KsmLocation: MultiLocation = MultiLocation::Null;
pub const KsmLocation: MultiLocation = MultiLocation::Here;
/// The Kusama network ID. This is named.
pub const KusamaNetwork: NetworkId = NetworkId::Kusama;
/// Our XCM location ancestry - i.e. what, if anything, `Parent` means evaluated in our context. Since
/// Kusama is a top-level relay-chain, there is no ancestry.
pub const Ancestry: MultiLocation = MultiLocation::Null;
pub const Ancestry: MultiLocation = MultiLocation::Here;
/// The check account, which holds any native assets that have been teleported out and not back in (yet).
pub CheckAccount: AccountId = XcmPallet::check_account();
}
@@ -1264,12 +1258,12 @@ parameter_types! {
/// individual routers.
pub type XcmRouter = (
// Only one router so far - use DMP to communicate with child parachains.
xcm_sender::ChildParachainRouter<Runtime>,
xcm_sender::ChildParachainRouter<Runtime, xcm::AlwaysRelease>,
);
parameter_types! {
pub const KusamaForStatemint: (MultiAsset, MultiLocation) =
(AllConcreteFungible { id: Null }, X1(Parachain(1000)));
pub const Kusama: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(KsmLocation::get()) });
pub const KusamaForStatemint: (MultiAssetFilter, MultiLocation) = (Kusama::get(), X1(Parachain(1000)));
}
pub type TrustedTeleporters = (xcm_builder::Case<KusamaForStatemint>,);
@@ -1317,65 +1311,14 @@ pub type LocalOriginToLocation = (
SignedToAccountId32<Origin, AccountId, KusamaNetwork>,
);
pub struct OnlyWithdrawTeleportForAccounts;
impl frame_support::traits::Contains<(MultiLocation, Xcm<Call>)>
for OnlyWithdrawTeleportForAccounts
{
fn contains((ref origin, ref msg): &(MultiLocation, Xcm<Call>)) -> bool {
use xcm::v0::{
Junction::{AccountId32, Plurality},
MultiAsset::{All, ConcreteFungible},
Order::{BuyExecution, DepositAsset, InitiateTeleport},
Xcm::WithdrawAsset,
};
match origin {
// Root and council are are allowed to execute anything.
Null | X1(Plurality { .. }) => true,
X1(AccountId32 { .. }) => {
// An account ID trying to send a message. We ensure that it's sensible.
// This checks that it's of the form:
// WithdrawAsset {
// assets: [ ConcreteFungible { id: Null } ],
// effects: [ BuyExecution, InitiateTeleport {
// assets: All,
// dest: Parachain,
// effects: [ BuyExecution, DepositAssets {
// assets: All,
// dest: AccountId32,
// } ]
// } ]
// }
matches!(msg, WithdrawAsset { ref assets, ref effects }
if assets.len() == 1
&& matches!(assets[0], ConcreteFungible { id: Null, .. })
&& effects.len() == 2
&& matches!(effects[0], BuyExecution { .. })
&& matches!(effects[1], InitiateTeleport { ref assets, dest: X1(Parachain(..)), ref effects }
if assets.len() == 1
&& matches!(assets[0], All)
&& effects.len() == 2
&& matches!(effects[0], BuyExecution { .. })
&& matches!(effects[1], DepositAsset { ref assets, dest: X1(AccountId32{..}) }
if assets.len() == 1
&& matches!(assets[0], All)
)
)
)
},
// Nobody else is allowed to execute anything.
_ => false,
}
}
}
impl pallet_xcm::Config for Runtime {
type Event = Event;
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
type XcmRouter = XcmRouter;
// Anyone can execute XCM messages locally...
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
// ...but they must match our filter, which requires them to be a simple withdraw + teleport.
type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts;
// ...but they must match our filter, which rejects all.
type XcmExecuteFilter = ();
type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
type XcmReserveTransferFilter = All<(MultiLocation, Vec<MultiAsset>)>;
+1 -1
View File
@@ -22,7 +22,7 @@ use frame_support::pallet_prelude::*;
use primitives::v1::{DownwardMessage, Hash, Id as ParaId, InboundDownwardMessage};
use sp_runtime::traits::{BlakeTwo256, Hash as HashT, SaturatedConversion};
use sp_std::{fmt, prelude::*};
use xcm::v0::Error as XcmError;
use xcm::latest::Error as XcmError;
pub use pallet::*;
+3 -3
View File
@@ -1007,7 +1007,7 @@ impl<T: Config> Pallet<T> {
let notification_bytes = {
use parity_scale_codec::Encode as _;
use xcm::opaque::{v0::Xcm, VersionedXcm};
use xcm::opaque::{v1::Xcm, VersionedXcm};
VersionedXcm::from(Xcm::HrmpNewChannelOpenRequest {
sender: u32::from(origin),
@@ -1066,7 +1066,7 @@ impl<T: Config> Pallet<T> {
let notification_bytes = {
use parity_scale_codec::Encode as _;
use xcm::opaque::{v0::Xcm, VersionedXcm};
use xcm::opaque::{v1::Xcm, VersionedXcm};
VersionedXcm::from(Xcm::HrmpChannelAccepted { recipient: u32::from(origin) }).encode()
};
@@ -1106,7 +1106,7 @@ impl<T: Config> Pallet<T> {
let config = <configuration::Pallet<T>>::config();
let notification_bytes = {
use parity_scale_codec::Encode as _;
use xcm::opaque::{v0::Xcm, VersionedXcm};
use xcm::opaque::{v1::Xcm, VersionedXcm};
VersionedXcm::from(Xcm::HrmpChannelClosing {
initiator: u32::from(origin),
+3 -3
View File
@@ -27,7 +27,7 @@ use sp_std::{
marker::PhantomData,
prelude::*,
};
use xcm::v0::Outcome;
use xcm::latest::Outcome;
pub use pallet::*;
@@ -78,14 +78,14 @@ pub type MessageId = [u8; 32];
/// and will be forwarded to the XCM Executor.
pub struct XcmSink<XcmExecutor, Config>(PhantomData<(XcmExecutor, Config)>);
impl<XcmExecutor: xcm::v0::ExecuteXcm<C::Call>, C: Config> UmpSink for XcmSink<XcmExecutor, C> {
impl<XcmExecutor: xcm::latest::ExecuteXcm<C::Call>, C: Config> UmpSink for XcmSink<XcmExecutor, C> {
fn process_upward_message(
origin: ParaId,
data: &[u8],
max_weight: Weight,
) -> Result<Weight, (MessageId, Weight)> {
use xcm::{
v0::{Error as XcmError, Junction, MultiLocation, Xcm},
latest::{Error as XcmError, Junction, MultiLocation, Xcm},
VersionedXcm,
};
+11 -71
View File
@@ -80,7 +80,7 @@ use polkadot_parachain::primitives::Id as ParaId;
use constants::{currency::*, fee::*, time::*};
use frame_support::traits::InstanceFilter;
use xcm::v0::{BodyId, MultiLocation, NetworkId, Xcm};
use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, BackingToPlurality, ChildParachainAsNative, ChildParachainConvertsVia,
ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedWeightBounds,
@@ -583,9 +583,9 @@ impl parachains_paras::Config for Runtime {
}
parameter_types! {
pub const RocLocation: MultiLocation = MultiLocation::Null;
pub const RocLocation: MultiLocation = MultiLocation::Here;
pub const RococoNetwork: NetworkId = NetworkId::Polkadot;
pub const Ancestry: MultiLocation = MultiLocation::Null;
pub const Ancestry: MultiLocation = MultiLocation::Here;
pub CheckAccount: AccountId = XcmPallet::check_account();
}
@@ -620,24 +620,15 @@ parameter_types! {
/// individual routers.
pub type XcmRouter = (
// Only one router so far - use DMP to communicate with child parachains.
xcm_sender::ChildParachainRouter<Runtime>,
xcm_sender::ChildParachainRouter<Runtime, xcm::AlwaysRelease>,
);
use xcm::v0::{
Junction::Parachain,
MultiAsset,
MultiAsset::AllConcreteFungible,
MultiLocation::{Null, X1},
};
parameter_types! {
pub const RococoForTick: (MultiAsset, MultiLocation) =
(AllConcreteFungible { id: Null }, X1(Parachain(100)));
pub const RococoForTrick: (MultiAsset, MultiLocation) =
(AllConcreteFungible { id: Null }, X1(Parachain(110)));
pub const RococoForTrack: (MultiAsset, MultiLocation) =
(AllConcreteFungible { id: Null }, X1(Parachain(120)));
pub const RococoForStatemint: (MultiAsset, MultiLocation) =
(AllConcreteFungible { id: Null }, X1(Parachain(1001)));
pub const Rococo: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(RocLocation::get()) });
pub const RococoForTick: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(100)));
pub const RococoForTrick: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(110)));
pub const RococoForTrack: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(120)));
pub const RococoForStatemint: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(1001)));
}
pub type TrustedTeleporters = (
xcm_builder::Case<RococoForTick>,
@@ -692,65 +683,14 @@ pub type LocalOriginToLocation = (
SignedToAccountId32<Origin, AccountId, RococoNetwork>,
);
pub struct OnlyWithdrawTeleportForAccounts;
impl frame_support::traits::Contains<(MultiLocation, Xcm<Call>)>
for OnlyWithdrawTeleportForAccounts
{
fn contains((ref origin, ref msg): &(MultiLocation, Xcm<Call>)) -> bool {
use xcm::v0::{
Junction::{AccountId32, Plurality},
MultiAsset::{All, ConcreteFungible},
Order::{BuyExecution, DepositAsset, InitiateTeleport},
Xcm::WithdrawAsset,
};
match origin {
// Root and collective are allowed to execute anything.
Null | X1(Plurality { .. }) => true,
X1(AccountId32 { .. }) => {
// An account ID trying to send a message. We ensure that it's sensible.
// This checks that it's of the form:
// WithdrawAsset {
// assets: [ ConcreteFungible { id: Null } ],
// effects: [ BuyExecution, InitiateTeleport {
// assets: All,
// dest: Parachain,
// effects: [ BuyExecution, DepositAssets {
// assets: All,
// dest: AccountId32,
// } ]
// } ]
// }
matches!(msg, WithdrawAsset { ref assets, ref effects }
if assets.len() == 1
&& matches!(assets[0], ConcreteFungible { id: Null, .. })
&& effects.len() == 2
&& matches!(effects[0], BuyExecution { .. })
&& matches!(effects[1], InitiateTeleport { ref assets, dest: X1(Parachain(..)), ref effects }
if assets.len() == 1
&& matches!(assets[0], All)
&& effects.len() == 2
&& matches!(effects[0], BuyExecution { .. })
&& matches!(effects[1], DepositAsset { ref assets, dest: X1(AccountId32{..}) }
if assets.len() == 1
&& matches!(assets[0], All)
)
)
)
},
// Nobody else is allowed to execute anything.
_ => false,
}
}
}
impl pallet_xcm::Config for Runtime {
type Event = Event;
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
type XcmRouter = XcmRouter;
// Anyone can execute XCM messages locally...
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
// ...but they must match our filter, which requires them to be a simple withdraw + teleport.
type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts;
// ...but they must match our filter, which right now rejects everything.
type XcmExecuteFilter = ();
type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
type XcmReserveTransferFilter = All<(MultiLocation, Vec<MultiAsset>)>;
+1 -1
View File
@@ -49,7 +49,7 @@ pub mod fee {
use frame_support::weights::{
WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial,
};
use primitives::v0::Balance;
use primitives::v1::Balance;
use runtime_common::ExtrinsicBaseWeight;
use smallvec::smallvec;
pub use sp_runtime::Perbill;
+8 -64
View File
@@ -44,12 +44,7 @@ use runtime_parachains::{
session_info as parachains_session_info, shared as parachains_shared, ump as parachains_ump,
};
use xcm::v0::{
Junction::Parachain,
MultiAsset::{self, AllConcreteFungible},
MultiLocation::{self, Null, X1},
NetworkId, Xcm,
};
use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom,
ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
@@ -871,8 +866,8 @@ impl auctions::Config for Runtime {
}
parameter_types! {
pub const WndLocation: MultiLocation = MultiLocation::Null;
pub const Ancestry: MultiLocation = MultiLocation::Null;
pub const WndLocation: MultiLocation = MultiLocation::Here;
pub const Ancestry: MultiLocation = MultiLocation::Here;
pub WestendNetwork: NetworkId = NetworkId::Named(b"Westend".to_vec());
pub CheckAccount: AccountId = XcmPallet::check_account();
}
@@ -908,12 +903,12 @@ parameter_types! {
/// individual routers.
pub type XcmRouter = (
// Only one router so far - use DMP to communicate with child parachains.
xcm_sender::ChildParachainRouter<Runtime>,
xcm_sender::ChildParachainRouter<Runtime, xcm::AlwaysRelease>,
);
parameter_types! {
pub const WestendForWestmint: (MultiAsset, MultiLocation) =
(AllConcreteFungible { id: Null }, X1(Parachain(1000)));
pub const WestendForWestmint: (MultiAssetFilter, MultiLocation) =
(Wild(AllOf { fun: WildFungible, id: Concrete(WndLocation::get()) }), X1(Parachain(1000)));
}
pub type TrustedTeleporters = (xcm_builder::Case<WestendForWestmint>,);
@@ -949,65 +944,14 @@ pub type LocalOriginToLocation = (
SignedToAccountId32<Origin, AccountId, WestendNetwork>,
);
pub struct OnlyWithdrawTeleportForAccounts;
impl frame_support::traits::Contains<(MultiLocation, Xcm<Call>)>
for OnlyWithdrawTeleportForAccounts
{
fn contains((ref origin, ref msg): &(MultiLocation, Xcm<Call>)) -> bool {
use xcm::v0::{
Junction::AccountId32,
MultiAsset::{All, ConcreteFungible},
Order::{BuyExecution, DepositAsset, InitiateTeleport},
Xcm::WithdrawAsset,
};
match origin {
// Root is allowed to execute anything.
Null => true,
X1(AccountId32 { .. }) => {
// An account ID trying to send a message. We ensure that it's sensible.
// This checks that it's of the form:
// WithdrawAsset {
// assets: [ ConcreteFungible { id: Null } ],
// effects: [ BuyExecution, InitiateTeleport {
// assets: All,
// dest: Parachain,
// effects: [ BuyExecution, DepositAssets {
// assets: All,
// dest: AccountId32,
// } ]
// } ]
// }
matches!(msg, WithdrawAsset { ref assets, ref effects }
if assets.len() == 1
&& matches!(assets[0], ConcreteFungible { id: Null, .. })
&& effects.len() == 2
&& matches!(effects[0], BuyExecution { .. })
&& matches!(effects[1], InitiateTeleport { ref assets, dest: X1(Parachain(..)), ref effects }
if assets.len() == 1
&& matches!(assets[0], All)
&& effects.len() == 2
&& matches!(effects[0], BuyExecution { .. })
&& matches!(effects[1], DepositAsset { ref assets, dest: X1(AccountId32{..}) }
if assets.len() == 1
&& matches!(assets[0], All)
)
)
)
},
// Nobody else is allowed to execute anything.
_ => false,
}
}
}
impl pallet_xcm::Config for Runtime {
type Event = Event;
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
type XcmRouter = XcmRouter;
// Anyone can execute XCM messages locally...
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
// ...but they must match our filter, which requires them to be a simple withdraw + teleport.
type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts;
// ...but they must match our filter, which rejects everything.
type XcmExecuteFilter = ();
type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
type XcmReserveTransferFilter = All<(MultiLocation, Vec<MultiAsset>)>;
+1
View File
@@ -74,6 +74,7 @@ fedora/M
finalize/B
FRAME/MS
FSMs
fungibility
gameable
getter/MS
GiB/S
+40 -23
View File
@@ -23,10 +23,11 @@ mod mock;
#[cfg(test)]
mod tests;
use codec::{Decode, Encode};
use frame_support::traits::{Contains, EnsureOrigin, Filter, Get, OriginTrait};
use sp_runtime::{traits::BadOrigin, RuntimeDebug};
use sp_std::{boxed::Box, convert::TryInto, marker::PhantomData, prelude::*, vec};
use xcm::v0::prelude::*;
use xcm::latest::prelude::*;
use xcm_executor::traits::ConvertOrigin;
use frame_support::PalletId;
@@ -87,7 +88,7 @@ pub mod pallet {
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
Attempted(xcm::v0::Outcome),
Attempted(xcm::latest::Outcome),
Sent(MultiLocation, MultiLocation, Xcm<()>),
}
@@ -134,15 +135,15 @@ pub mod pallet {
/// from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain.
/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be
/// an `AccountId32` value.
/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the
/// `dest` side.
/// - `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the
/// `dest` side. May not be empty.
/// - `dest_weight`: Equal to the total weight on `dest` of the XCM message
/// `Teleport { assets, effects: [ BuyExecution{..}, DepositAsset{..} ] }`.
#[pallet::weight({
let mut message = Xcm::WithdrawAsset {
assets: assets.clone(),
effects: sp_std::vec![ InitiateTeleport {
assets: sp_std::vec![ All ],
assets: Wild(All),
dest: dest.clone(),
effects: sp_std::vec![],
} ]
@@ -153,21 +154,28 @@ pub mod pallet {
origin: OriginFor<T>,
dest: MultiLocation,
beneficiary: MultiLocation,
assets: Vec<MultiAsset>,
assets: MultiAssets,
fee_asset_item: u32,
dest_weight: Weight,
) -> DispatchResult {
let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
let value = (origin_location, assets);
let value = (origin_location, assets.drain());
ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
let (origin_location, assets) = value;
let inv_dest = T::LocationInverter::invert_location(&dest);
let mut fees = assets.first().ok_or(Error::<T>::Empty)?.clone();
fees.reanchor(&inv_dest).map_err(|_| Error::<T>::CannotReanchor)?;
let fees = assets
.get(fee_asset_item as usize)
.ok_or(Error::<T>::Empty)?
.clone()
.reanchored(&inv_dest)
.map_err(|_| Error::<T>::CannotReanchor)?;
let max_assets = assets.len() as u32;
let assets = assets.into();
let mut message = Xcm::WithdrawAsset {
assets,
effects: vec![InitiateTeleport {
assets: vec![All],
assets: Wild(All),
dest,
effects: vec![
BuyExecution {
@@ -176,9 +184,10 @@ pub mod pallet {
weight: 0,
debt: dest_weight,
halt_on_error: false,
xcm: vec![],
orders: vec![],
instructions: vec![],
},
DepositAsset { assets: vec![All], dest: beneficiary },
DepositAsset { assets: Wild(All), max_assets, beneficiary },
],
}],
};
@@ -203,7 +212,7 @@ pub mod pallet {
/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the
/// `dest` side.
/// - `dest_weight`: Equal to the total weight on `dest` of the XCM message
/// `ReserveAssetDeposit { assets, effects: [ BuyExecution{..}, DepositAsset{..} ] }`.
/// `ReserveAssetDeposited { assets, effects: [ BuyExecution{..}, DepositAsset{..} ] }`.
#[pallet::weight({
let mut message = Xcm::TransferReserveAsset {
assets: assets.clone(),
@@ -216,30 +225,38 @@ pub mod pallet {
origin: OriginFor<T>,
dest: MultiLocation,
beneficiary: MultiLocation,
assets: Vec<MultiAsset>,
assets: MultiAssets,
fee_asset_item: u32,
dest_weight: Weight,
) -> DispatchResult {
let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
let value = (origin_location, assets);
let value = (origin_location, assets.drain());
ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
let (origin_location, assets) = value;
let inv_dest = T::LocationInverter::invert_location(&dest);
let mut fees = assets.first().ok_or(Error::<T>::Empty)?.clone();
fees.reanchor(&inv_dest).map_err(|_| Error::<T>::CannotReanchor)?;
let fees = assets
.get(fee_asset_item as usize)
.ok_or(Error::<T>::Empty)?
.clone()
.reanchored(&inv_dest)
.map_err(|_| Error::<T>::CannotReanchor)?;
let max_assets = assets.len() as u32;
let assets = assets.into();
let mut message = Xcm::TransferReserveAsset {
assets,
dest,
effects: vec![
BuyExecution {
fees,
// Zero weight for additional XCM (since there are none to execute)
// Zero weight for additional instructions/orders (since there are none to execute)
weight: 0,
debt: dest_weight,
debt: dest_weight, // covers this, `TransferReserveAsset` xcm, and `DepositAsset` order.
halt_on_error: false,
xcm: vec![],
orders: vec![],
instructions: vec![],
},
DepositAsset { assets: vec![All], dest: beneficiary },
DepositAsset { assets: Wild(All), max_assets, beneficiary },
],
};
let weight =
@@ -286,7 +303,7 @@ pub mod pallet {
message: Xcm<()>,
) -> Result<(), XcmError> {
let message = match interior {
MultiLocation::Null => message,
MultiLocation::Here => message,
who => Xcm::<()>::RelayedFrom { who, message: Box::new(message) },
};
log::trace!(target: "xcm::send_xcm", "dest: {:?}, message: {:?}", &dest, &message);
@@ -363,7 +380,7 @@ where
#[cfg(feature = "runtime-benchmarks")]
fn successful_origin() -> O {
O::from(Origin::Xcm(MultiLocation::Null))
O::from(Origin::Xcm(MultiLocation::Here))
}
}
+20 -13
View File
@@ -25,15 +25,15 @@ use sp_core::H256;
use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32};
pub use sp_std::{cell::RefCell, fmt::Debug, marker::PhantomData};
use xcm::{
opaque::v0::{Error as XcmError, MultiAsset, Result as XcmResult, SendXcm, Xcm},
v0::{MultiLocation, NetworkId, Order},
latest::prelude::*,
opaque::latest::{Error as XcmError, MultiAsset, Result as XcmResult, SendXcm, Xcm},
};
use xcm_builder::{
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative,
ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfConcreteFungible, FixedWeightBounds,
IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit,
CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete,
LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation,
TakeWeightCredit,
};
use xcm_executor::XcmExecutor;
@@ -133,9 +133,9 @@ impl pallet_balances::Config for Test {
}
parameter_types! {
pub const RelayLocation: MultiLocation = MultiLocation::Null;
pub const RelayLocation: MultiLocation = MultiLocation::Here;
pub const AnyNetwork: NetworkId = NetworkId::Any;
pub Ancestry: MultiLocation = MultiLocation::Null;
pub Ancestry: MultiLocation = MultiLocation::Here;
pub UnitWeightCost: Weight = 1_000;
}
@@ -154,7 +154,7 @@ type LocalOriginConverter = (
parameter_types! {
pub const BaseXcmWeight: Weight = 1_000;
pub CurrencyPerSecond: (MultiLocation, u128) = (RelayLocation::get(), 1);
pub CurrencyPerSecond: (AssetId, u128) = (Concrete(RelayLocation::get()), 1);
}
pub type Barrier = (TakeWeightCredit, AllowTopLevelPaidExecutionFrom<All<MultiLocation>>);
@@ -170,7 +170,7 @@ impl xcm_executor::Config for XcmConfig {
type LocationInverter = LocationInverter<Ancestry>;
type Barrier = Barrier;
type Weigher = FixedWeightBounds<BaseXcmWeight, Call>;
type Trader = FixedRateOfConcreteFungible<CurrencyPerSecond, ()>;
type Trader = FixedRateOfFungible<CurrencyPerSecond, ()>;
type ResponseHandler = ();
}
@@ -181,7 +181,7 @@ impl pallet_xcm::Config for Test {
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
type XcmRouter = (TestSendXcmErrX8, TestSendXcm);
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
type XcmExecuteFilter = All<(MultiLocation, xcm::v0::Xcm<Call>)>;
type XcmExecuteFilter = All<(MultiLocation, xcm::latest::Xcm<Call>)>;
type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
type XcmReserveTransferFilter = All<(MultiLocation, Vec<MultiAsset>)>;
@@ -195,9 +195,16 @@ pub(crate) fn last_event() -> Event {
System::events().pop().expect("Event expected").event
}
pub(crate) fn buy_execution<C>(debt: Weight, fees: MultiAsset) -> Order<C> {
use xcm::opaque::v0::prelude::*;
Order::BuyExecution { fees, weight: 0, debt, halt_on_error: false, xcm: vec![] }
pub(crate) fn buy_execution<C>(fees: impl Into<MultiAsset>, debt: Weight) -> Order<C> {
use xcm::opaque::latest::prelude::*;
Order::BuyExecution {
fees: fees.into(),
weight: 0,
debt,
halt_on_error: false,
orders: vec![],
instructions: vec![],
}
}
pub(crate) fn new_test_ext_with_balances(
+24 -34
View File
@@ -18,8 +18,8 @@ use crate::mock::*;
use frame_support::{assert_noop, assert_ok, traits::Currency};
use polkadot_parachain::primitives::{AccountIdConversion, Id as ParaId};
use xcm::{
opaque::v0::prelude::*,
v0::{Junction, Xcm},
opaque::v1::prelude::*,
v1::{Junction, Xcm},
};
const ALICE: AccountId = AccountId::new([0u8; 32]);
@@ -38,22 +38,19 @@ fn send_works() {
new_test_ext_with_balances(balances).execute_with(|| {
let weight = 2 * BaseXcmWeight::get();
let sender: MultiLocation =
Junction::AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into();
let message = Xcm::ReserveAssetDeposit {
assets: vec![ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT }],
AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into();
let message = Xcm::ReserveAssetDeposited {
assets: (X1(Parent), SEND_AMOUNT).into(),
effects: vec![
buy_execution(
weight,
ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT },
),
DepositAsset { assets: vec![All], dest: sender.clone() },
buy_execution((Parent, SEND_AMOUNT), weight),
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() },
],
};
assert_ok!(XcmPallet::send(Origin::signed(ALICE), RelayLocation::get(), message.clone()));
assert_eq!(
sent_xcm(),
vec![(
MultiLocation::Null,
MultiLocation::Here,
RelayedFrom { who: sender.clone(), message: Box::new(message.clone()) }
)]
);
@@ -76,14 +73,11 @@ fn send_fails_when_xcm_router_blocks() {
let weight = 2 * BaseXcmWeight::get();
let sender: MultiLocation =
Junction::AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into();
let message = Xcm::ReserveAssetDeposit {
assets: vec![ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT }],
let message = Xcm::ReserveAssetDeposited {
assets: (Parent, SEND_AMOUNT).into(),
effects: vec![
buy_execution(
weight,
ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT },
),
DepositAsset { assets: vec![All], dest: sender.clone() },
buy_execution((Parent, SEND_AMOUNT), weight),
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() },
],
};
assert_noop!(
@@ -120,8 +114,9 @@ fn teleport_assets_works() {
assert_ok!(XcmPallet::teleport_assets(
Origin::signed(ALICE),
RelayLocation::get(),
MultiLocation::X1(Junction::AccountId32 { network: NetworkId::Any, id: BOB.into() }),
vec![ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT }],
X1(AccountId32 { network: Any, id: BOB.into() }),
(Here, SEND_AMOUNT).into(),
0,
weight,
));
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT);
@@ -149,7 +144,8 @@ fn reserve_transfer_assets_works() {
Origin::signed(ALICE),
Parachain(PARA_ID).into(),
dest.clone(),
vec![ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT }],
(Here, SEND_AMOUNT).into(),
0,
weight
));
// Alice spent amount
@@ -161,14 +157,11 @@ fn reserve_transfer_assets_works() {
sent_xcm(),
vec![(
Parachain(PARA_ID).into(),
Xcm::ReserveAssetDeposit {
assets: vec![ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT }],
Xcm::ReserveAssetDeposited {
assets: (X1(Parent), SEND_AMOUNT).into(),
effects: vec![
buy_execution(
weight,
ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT }
),
DepositAsset { assets: vec![All], dest },
buy_execution((Parent, SEND_AMOUNT), weight),
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest },
]
}
)]
@@ -196,13 +189,10 @@ fn execute_withdraw_to_deposit_works() {
assert_ok!(XcmPallet::execute(
Origin::signed(ALICE),
Box::new(Xcm::WithdrawAsset {
assets: vec![ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT }],
assets: (Here, SEND_AMOUNT).into(),
effects: vec![
buy_execution(
weight,
ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT }
),
DepositAsset { assets: vec![All], dest },
buy_execution((Here, SEND_AMOUNT), weight),
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest }
],
}),
weight
+108 -13
View File
@@ -23,14 +23,32 @@
#![no_std]
extern crate alloc;
use core::{
convert::{TryFrom, TryInto},
result::Result,
};
use derivative::Derivative;
use parity_scale_codec::{Decode, Encode};
use parity_scale_codec::{Decode, Encode, Error as CodecError, Input};
pub mod v0;
pub mod v1;
pub mod latest {
pub use super::v1::*;
}
mod double_encoded;
pub use double_encoded::DoubleEncoded;
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum Unsupported {}
impl Encode for Unsupported {}
impl Decode for Unsupported {
fn decode<I: Input>(_: &mut I) -> Result<Self, CodecError> {
Err("Not decodable".into())
}
}
/// A single XCM message, together with its version code.
#[derive(Derivative, Encode, Decode)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
@@ -38,8 +56,87 @@ pub use double_encoded::DoubleEncoded;
#[codec(decode_bound())]
pub enum VersionedXcm<Call> {
V0(v0::Xcm<Call>),
V1(v1::Xcm<Call>),
}
impl<Call> From<v0::Xcm<Call>> for VersionedXcm<Call> {
fn from(x: v0::Xcm<Call>) -> Self {
VersionedXcm::V0(x)
}
}
impl<Call> From<v1::Xcm<Call>> for VersionedXcm<Call> {
fn from(x: v1::Xcm<Call>) -> Self {
VersionedXcm::V1(x)
}
}
impl<Call> TryFrom<VersionedXcm<Call>> for v0::Xcm<Call> {
type Error = ();
fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
match x {
VersionedXcm::V0(x) => Ok(x),
VersionedXcm::V1(x) => x.try_into(),
}
}
}
impl<Call> TryFrom<VersionedXcm<Call>> for v1::Xcm<Call> {
type Error = ();
fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
match x {
VersionedXcm::V0(x) => x.try_into(),
VersionedXcm::V1(x) => Ok(x),
}
}
}
/// Convert an `Xcm` datum into a `VersionedXcm`, based on a destination `MultiLocation` which will interpret it.
pub trait WrapVersion {
fn wrap_version<Call>(
dest: &latest::MultiLocation,
xcm: impl Into<VersionedXcm<Call>>,
) -> Result<VersionedXcm<Call>, ()>;
}
/// `()` implementation does nothing with the XCM, just sending with whatever version it was authored as.
impl WrapVersion for () {
fn wrap_version<Call>(
_: &latest::MultiLocation,
xcm: impl Into<VersionedXcm<Call>>,
) -> Result<VersionedXcm<Call>, ()> {
Ok(xcm.into())
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to version 0 before wrapping it.
pub struct AlwaysV0;
impl WrapVersion for AlwaysV0 {
fn wrap_version<Call>(
_: &latest::MultiLocation,
xcm: impl Into<VersionedXcm<Call>>,
) -> Result<VersionedXcm<Call>, ()> {
Ok(VersionedXcm::<Call>::V0(xcm.into().try_into()?))
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to version 1 before wrapping it.
pub struct AlwaysV1;
impl WrapVersion for AlwaysV1 {
fn wrap_version<Call>(
_: &latest::MultiLocation,
xcm: impl Into<VersionedXcm<Call>>,
) -> Result<VersionedXcm<Call>, ()> {
Ok(VersionedXcm::<Call>::V1(xcm.into().try_into()?))
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to the latest version before wrapping it.
pub type AlwaysLatest = AlwaysV1;
/// `WrapVersion` implementation which attempts to always convert the XCM to the release version before wrapping it.
pub type AlwaysRelease = AlwaysV0;
pub mod opaque {
pub mod v0 {
// Everything from v0
@@ -47,19 +144,17 @@ pub mod opaque {
// Then override with the opaque types in v0
pub use crate::v0::opaque::{Order, Xcm};
}
pub mod v1 {
// Everything from v1
pub use crate::v1::*;
// Then override with the opaque types in v1
pub use crate::v1::opaque::{Order, Xcm};
}
pub mod latest {
pub use super::v1::*;
}
/// The basic `VersionedXcm` type which just uses the `Vec<u8>` as an encoded call.
pub type VersionedXcm = super::VersionedXcm<()>;
}
/// A versioned multi-location, a relative location of a cross-consensus system identifier.
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)]
pub enum VersionedMultiLocation {
V0(v0::MultiLocation),
}
/// A versioned multi-asset, an identifier for an asset within a consensus system.
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)]
pub enum VersionedMultiAsset {
V0(v0::MultiAsset),
}
+63 -53
View File
@@ -16,18 +16,20 @@
//! Version 0 of the Cross-Consensus Message format data structures.
use crate::{DoubleEncoded, VersionedMultiAsset, VersionedXcm};
use crate::DoubleEncoded;
use alloc::vec::Vec;
use core::{convert::TryFrom, fmt::Debug, result};
use core::{
convert::{TryFrom, TryInto},
result,
};
use derivative::Derivative;
use parity_scale_codec::{self, Decode, Encode};
mod junction;
mod multi_asset;
mod multi_location;
mod order;
mod traits;
pub use junction::{BodyId, BodyPart, Junction, NetworkId};
use super::v1::Xcm as Xcm1;
pub use multi_asset::{AssetInstance, MultiAsset};
pub use multi_location::MultiLocation;
pub use order::Order;
@@ -36,7 +38,6 @@ pub use traits::{Error, ExecuteXcm, Outcome, Result, SendXcm};
/// A prelude for importing all types typically used when interacting with XCM messages.
pub mod prelude {
pub use super::{
junction::{BodyId, BodyPart, Junction::*, NetworkId},
multi_asset::{
AssetInstance::{self, *},
MultiAsset::{self, *},
@@ -44,43 +45,14 @@ pub mod prelude {
multi_location::MultiLocation::{self, *},
order::Order::{self, *},
traits::{Error as XcmError, ExecuteXcm, Outcome, Result as XcmResult, SendXcm},
OriginKind,
BodyId, BodyPart,
Junction::*,
NetworkId, OriginKind,
Xcm::{self, *},
};
}
// TODO: #2841 #XCMENCODE Efficient encodings for Vec<MultiAsset>, Vec<Order>, using initial byte values 128+ to encode
// the number of items in the vector.
/// Basically just the XCM (more general) version of `ParachainDispatchOrigin`.
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug)]
pub enum OriginKind {
/// Origin should just be the native dispatch origin representation for the sender in the
/// local runtime framework. For Cumulus/Frame chains this is the `Parachain` or `Relay` origin
/// if coming from a chain, though there may be others if the `MultiLocation` XCM origin has a
/// primary/native dispatch origin form.
Native,
/// Origin should just be the standard account-based origin with the sovereign account of
/// the sender. For Cumulus/Frame chains, this is the `Signed` origin.
SovereignAccount,
/// Origin should be the super-user. For Cumulus/Frame chains, this is the `Root` origin.
/// This will not usually be an available option.
Superuser,
/// Origin should be interpreted as an XCM native origin and the `MultiLocation` should be
/// encoded directly in the dispatch origin unchanged. For Cumulus/Frame chains, this will be
/// the `pallet_xcm::Origin::Xcm` type.
Xcm,
}
/// Response data to a query.
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)]
pub enum Response {
/// Some assets.
Assets(Vec<MultiAsset>),
}
pub use super::v1::{BodyId, BodyPart, Junction, NetworkId, OriginKind, Response};
/// Cross-Consensus Message: A message from one consensus system to another.
///
@@ -275,21 +247,6 @@ pub enum Xcm<Call> {
RelayedFrom { who: MultiLocation, message: alloc::boxed::Box<Xcm<Call>> },
}
impl<Call> From<Xcm<Call>> for VersionedXcm<Call> {
fn from(x: Xcm<Call>) -> Self {
VersionedXcm::V0(x)
}
}
impl<Call> TryFrom<VersionedXcm<Call>> for Xcm<Call> {
type Error = ();
fn try_from(x: VersionedXcm<Call>) -> result::Result<Self, ()> {
match x {
VersionedXcm::V0(x) => Ok(x),
}
}
}
impl<Call> Xcm<Call> {
pub fn into<C>(self) -> Xcm<C> {
Xcm::from(self)
@@ -329,3 +286,56 @@ pub mod opaque {
pub use super::order::opaque::*;
}
impl<Call> TryFrom<Xcm1<Call>> for Xcm<Call> {
type Error = ();
fn try_from(x: Xcm1<Call>) -> result::Result<Xcm<Call>, ()> {
use Xcm::*;
Ok(match x {
Xcm1::WithdrawAsset { assets, effects } => WithdrawAsset {
assets: assets.into(),
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm1::ReserveAssetDeposited { assets, effects } => ReserveAssetDeposit {
assets: assets.into(),
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm1::ReceiveTeleportedAsset { assets, effects } => TeleportAsset {
assets: assets.into(),
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm1::QueryResponse { query_id: u64, response } =>
QueryResponse { query_id: u64, response },
Xcm1::TransferAsset { assets, beneficiary } =>
TransferAsset { assets: assets.into(), dest: beneficiary.into() },
Xcm1::TransferReserveAsset { assets, dest, effects } => TransferReserveAsset {
assets: assets.into(),
dest: dest.into(),
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm1::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
Xcm1::HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
Xcm1::HrmpChannelClosing { initiator, sender, recipient } =>
HrmpChannelClosing { initiator, sender, recipient },
Xcm1::Transact { origin_type, require_weight_at_most, call } =>
Transact { origin_type, require_weight_at_most, call: call.into() },
Xcm1::RelayedFrom { who, message } => RelayedFrom {
who: who.into(),
message: alloc::boxed::Box::new((*message).try_into()?),
},
})
}
}
+63 -64
View File
@@ -16,40 +16,12 @@
//! Cross-Consensus Message format data structures.
use alloc::vec::Vec;
use core::{convert::TryFrom, result};
use super::{MultiLocation, VersionedMultiAsset};
use super::MultiLocation;
use crate::v1::{MultiAsset as MultiAsset1, MultiAssetFilter, MultiAssets, WildMultiAsset};
use alloc::{vec, vec::Vec};
use parity_scale_codec::{self, Decode, Encode};
/// A general identifier for an instance of a non-fungible asset class.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)]
pub enum AssetInstance {
/// Undefined - used if the NFA class has only one instance.
Undefined,
/// A compact index. Technically this could be greater than `u128`, but this implementation supports only
/// values up to `2**128 - 1`.
Index {
#[codec(compact)]
id: u128,
},
/// A 4-byte fixed-length datum.
Array4([u8; 4]),
/// An 8-byte fixed-length datum.
Array8([u8; 8]),
/// A 16-byte fixed-length datum.
Array16([u8; 16]),
/// A 32-byte fixed-length datum.
Array32([u8; 32]),
/// An arbitrary piece of data. Use only when necessary.
Blob(Vec<u8>),
}
pub use crate::v1::AssetInstance;
/// A single general identifier for an asset.
///
@@ -318,17 +290,53 @@ impl MultiAsset {
}
}
impl From<MultiAsset> for VersionedMultiAsset {
fn from(x: MultiAsset) -> Self {
VersionedMultiAsset::V0(x)
impl From<MultiAsset1> for MultiAsset {
fn from(a: MultiAsset1) -> MultiAsset {
use crate::v1::{AssetId::*, Fungibility::*};
use MultiAsset::*;
match (a.id, a.fun) {
(Concrete(id), Fungible(amount)) => ConcreteFungible { id: id.into(), amount },
(Concrete(class), NonFungible(instance)) =>
ConcreteNonFungible { class: class.into(), instance },
(Abstract(id), Fungible(amount)) => AbstractFungible { id, amount },
(Abstract(class), NonFungible(instance)) => AbstractNonFungible { class, instance },
}
}
}
impl TryFrom<VersionedMultiAsset> for MultiAsset {
type Error = ();
fn try_from(x: VersionedMultiAsset) -> result::Result<Self, ()> {
match x {
VersionedMultiAsset::V0(x) => Ok(x),
impl From<MultiAssets> for Vec<MultiAsset> {
fn from(a: MultiAssets) -> Vec<MultiAsset> {
a.drain().into_iter().map(MultiAsset::from).collect()
}
}
impl From<WildMultiAsset> for MultiAsset {
fn from(a: WildMultiAsset) -> MultiAsset {
use crate::v1::{AssetId::*, WildFungibility::*};
use MultiAsset::*;
match a {
WildMultiAsset::All => All,
WildMultiAsset::AllOf { id, fun } => match (id, fun) {
(Concrete(id), Fungible) => AllConcreteFungible { id: id.into() },
(Concrete(class), NonFungible) => AllConcreteNonFungible { class: class.into() },
(Abstract(id), Fungible) => AllAbstractFungible { id },
(Abstract(class), NonFungible) => AllAbstractNonFungible { class },
},
}
}
}
impl From<WildMultiAsset> for Vec<MultiAsset> {
fn from(a: WildMultiAsset) -> Vec<MultiAsset> {
vec![a.into()]
}
}
impl From<MultiAssetFilter> for Vec<MultiAsset> {
fn from(a: MultiAssetFilter) -> Vec<MultiAsset> {
match a {
MultiAssetFilter::Definite(assets) => assets.into(),
MultiAssetFilter::Wild(wildcard) => wildcard.into(),
}
}
}
@@ -367,29 +375,20 @@ mod tests {
.contains(&AbstractFungible { id: vec![99u8], amount: 100 }));
// For non-fungibles, containing is equality.
assert!(!AbstractNonFungible {
class: vec![99u8],
instance: AssetInstance::Index { id: 9 }
}
.contains(&AbstractNonFungible {
class: vec![98u8],
instance: AssetInstance::Index { id: 9 }
}));
assert!(!AbstractNonFungible {
class: vec![99u8],
instance: AssetInstance::Index { id: 8 }
}
.contains(&AbstractNonFungible {
class: vec![99u8],
instance: AssetInstance::Index { id: 9 }
}));
assert!(AbstractNonFungible {
class: vec![99u8],
instance: AssetInstance::Index { id: 9 }
}
.contains(&AbstractNonFungible {
class: vec![99u8],
instance: AssetInstance::Index { id: 9 }
}));
assert!(!AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(9) }
.contains(&AbstractNonFungible {
class: vec![98u8],
instance: AssetInstance::Index(9)
}));
assert!(!AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(8) }
.contains(&AbstractNonFungible {
class: vec![99u8],
instance: AssetInstance::Index(9)
}));
assert!(AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(9) }
.contains(&AbstractNonFungible {
class: vec![99u8],
instance: AssetInstance::Index(9)
}));
}
}
+16 -14
View File
@@ -16,10 +16,9 @@
//! Cross-Consensus Message format data structures.
use core::{convert::TryFrom, mem, result};
use core::{mem, result};
use super::Junction;
use crate::VersionedMultiLocation;
use super::{super::v1::MultiLocation as MultiLocation1, Junction};
use parity_scale_codec::{self, Decode, Encode};
/// A relative path between state-bearing consensus systems.
@@ -697,17 +696,20 @@ impl MultiLocation {
}
}
impl From<MultiLocation> for VersionedMultiLocation {
fn from(x: MultiLocation) -> Self {
VersionedMultiLocation::V0(x)
}
}
impl TryFrom<VersionedMultiLocation> for MultiLocation {
type Error = ();
fn try_from(x: VersionedMultiLocation) -> result::Result<Self, ()> {
match x {
VersionedMultiLocation::V0(x) => Ok(x),
impl From<MultiLocation1> for MultiLocation {
fn from(old: MultiLocation1) -> Self {
use MultiLocation::*;
match old {
MultiLocation1::Here => Null,
MultiLocation1::X1(j0) => X1(j0),
MultiLocation1::X2(j0, j1) => X2(j0, j1),
MultiLocation1::X3(j0, j1, j2) => X3(j0, j1, j2),
MultiLocation1::X4(j0, j1, j2, j3) => X4(j0, j1, j2, j3),
MultiLocation1::X5(j0, j1, j2, j3, j4) => X5(j0, j1, j2, j3, j4),
MultiLocation1::X6(j0, j1, j2, j3, j4, j5) => X6(j0, j1, j2, j3, j4, j5),
MultiLocation1::X7(j0, j1, j2, j3, j4, j5, j6) => X7(j0, j1, j2, j3, j4, j5, j6),
MultiLocation1::X8(j0, j1, j2, j3, j4, j5, j6, j7) =>
X8(j0, j1, j2, j3, j4, j5, j6, j7),
}
}
}
+53 -1
View File
@@ -16,8 +16,9 @@
//! Version 0 of the Cross-Consensus Message format data structures.
use super::{MultiAsset, MultiLocation, Xcm};
use super::{super::v1::Order as Order1, MultiAsset, MultiLocation, Xcm};
use alloc::vec::Vec;
use core::{convert::TryFrom, result};
use derivative::Derivative;
use parity_scale_codec::{self, Decode, Encode};
@@ -152,3 +153,54 @@ impl<Call> Order<Call> {
}
}
}
impl<Call> TryFrom<Order1<Call>> for Order<Call> {
type Error = ();
fn try_from(old: Order1<Call>) -> result::Result<Order<Call>, ()> {
use Order::*;
Ok(match old {
Order1::Noop => Null,
Order1::DepositAsset { assets, beneficiary, .. } =>
DepositAsset { assets: assets.into(), dest: beneficiary.into() },
Order1::DepositReserveAsset { assets, dest, effects, .. } => DepositReserveAsset {
assets: assets.into(),
dest: dest.into(),
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Order1::ExchangeAsset { give, receive } =>
ExchangeAsset { give: give.into(), receive: receive.into() },
Order1::InitiateReserveWithdraw { assets, reserve, effects } =>
InitiateReserveWithdraw {
assets: assets.into(),
reserve: reserve.into(),
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Order1::InitiateTeleport { assets, dest, effects } => InitiateTeleport {
assets: assets.into(),
dest: dest.into(),
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Order1::QueryHolding { query_id, dest, assets } =>
QueryHolding { query_id, dest: dest.into(), assets: assets.into() },
Order1::BuyExecution { fees, weight, debt, halt_on_error, orders, instructions } => {
if !orders.is_empty() {
return Err(())
}
let xcm = instructions
.into_iter()
.map(Xcm::<Call>::try_from)
.collect::<result::Result<_, _>>()?;
BuyExecution { fees: fees.into(), weight, debt, halt_on_error, xcm }
},
})
}
}
@@ -40,11 +40,7 @@ pub enum BodyId {
/// A named body.
Named(Vec<u8>),
/// An indexed body.
// TODO: parity-scale-codec#262: Change to be a tuple.
Index {
#[codec(compact)]
id: u32,
},
Index(#[codec(compact)] u32),
/// The unambiguous executive body (for Polkadot, this would be the Polkadot council).
Executive,
/// The unambiguous technical body (for Polkadot, this would be the Technical Committee).
+392
View File
@@ -0,0 +1,392 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
//! Version 1 of the Cross-Consensus Message format data structures.
use super::v0::Xcm as Xcm0;
use crate::DoubleEncoded;
use alloc::vec::Vec;
use core::{
convert::{TryFrom, TryInto},
fmt::Debug,
result,
};
use derivative::Derivative;
use parity_scale_codec::{self, Decode, Encode};
mod junction;
pub mod multiasset;
mod multilocation;
mod order;
mod traits; // the new multiasset.
pub use junction::{BodyId, BodyPart, Junction, NetworkId};
pub use multiasset::{
AssetId, AssetInstance, Fungibility, MultiAsset, MultiAssetFilter, MultiAssets,
WildFungibility, WildMultiAsset,
};
pub use multilocation::MultiLocation;
pub use order::Order;
pub use traits::{Error, ExecuteXcm, Outcome, Result, SendXcm};
/// A prelude for importing all types typically used when interacting with XCM messages.
pub mod prelude {
pub use super::{
junction::{
BodyId, BodyPart,
Junction::*,
NetworkId::{self, *},
},
multiasset::{
AssetId::{self, *},
AssetInstance::{self, *},
Fungibility::{self, *},
MultiAsset,
MultiAssetFilter::{self, *},
MultiAssets,
WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible},
WildMultiAsset::{self, *},
},
multilocation::MultiLocation::{self, *},
opaque,
order::Order::{self, *},
traits::{Error as XcmError, ExecuteXcm, Outcome, Result as XcmResult, SendXcm},
OriginKind, Response,
Xcm::{self, *},
};
}
// TODO: #2841 #XCMENCODE Efficient encodings for MultiAssets, Vec<Order>, using initial byte values 128+ to encode
// the number of items in the vector.
/// Basically just the XCM (more general) version of `ParachainDispatchOrigin`.
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug)]
pub enum OriginKind {
/// Origin should just be the native dispatch origin representation for the sender in the
/// local runtime framework. For Cumulus/Frame chains this is the `Parachain` or `Relay` origin
/// if coming from a chain, though there may be others if the `MultiLocation` XCM origin has a
/// primary/native dispatch origin form.
Native,
/// Origin should just be the standard account-based origin with the sovereign account of
/// the sender. For Cumulus/Frame chains, this is the `Signed` origin.
SovereignAccount,
/// Origin should be the super-user. For Cumulus/Frame chains, this is the `Root` origin.
/// This will not usually be an available option.
Superuser,
/// Origin should be interpreted as an XCM native origin and the `MultiLocation` should be
/// encoded directly in the dispatch origin unchanged. For Cumulus/Frame chains, this will be
/// the `pallet_xcm::Origin::Xcm` type.
Xcm,
}
/// Response data to a query.
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)]
pub enum Response {
/// Some assets.
Assets(MultiAssets),
}
/// Cross-Consensus Message: A message from one consensus system to another.
///
/// Consensus systems that may send and receive messages include blockchains and smart contracts.
///
/// All messages are delivered from a known *origin*, expressed as a `MultiLocation`.
///
/// This is the inner XCM format and is version-sensitive. Messages are typically passed using the outer
/// XCM format, known as `VersionedXcm`.
#[derive(Derivative, Encode, Decode)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
pub enum Xcm<Call> {
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place them into `holding`. Execute the
/// orders (`effects`).
///
/// - `assets`: The asset(s) to be withdrawn into holding.
/// - `effects`: The order(s) to execute on the holding register.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 0)]
WithdrawAsset { assets: MultiAssets, effects: Vec<Order<Call>> },
/// Asset(s) (`assets`) have been received into the ownership of this system on the `origin` system.
///
/// Some orders are given (`effects`) which should be executed once the corresponding derivative assets have
/// been placed into `holding`.
///
/// - `assets`: The asset(s) that are minted into holding.
/// - `effects`: The order(s) to execute on the holding register.
///
/// Safety: `origin` must be trusted to have received and be storing `assets` such that they may later be
/// withdrawn should this system send a corresponding message.
///
/// Kind: *Trusted Indication*.
///
/// Errors:
#[codec(index = 1)]
ReserveAssetDeposited { assets: MultiAssets, effects: Vec<Order<Call>> },
/// Asset(s) (`assets`) have been destroyed on the `origin` system and equivalent assets should be
/// created on this system.
///
/// Some orders are given (`effects`) which should be executed once the corresponding derivative assets have
/// been placed into the Holding Register.
///
/// - `assets`: The asset(s) that are minted into the Holding Register.
/// - `effects`: The order(s) to execute on the Holding Register.
///
/// Safety: `origin` must be trusted to have irrevocably destroyed the corresponding `assets` prior as a consequence
/// of sending this message.
///
/// Kind: *Trusted Indication*.
///
/// Errors:
#[codec(index = 2)]
ReceiveTeleportedAsset { assets: MultiAssets, effects: Vec<Order<Call>> },
/// Indication of the contents of the holding register corresponding to the `QueryHolding` order of `query_id`.
///
/// - `query_id`: The identifier of the query that resulted in this message being sent.
/// - `assets`: The message content.
///
/// Safety: No concerns.
///
/// Kind: *Information*.
///
/// Errors:
#[codec(index = 3)]
QueryResponse {
#[codec(compact)]
query_id: u64,
response: Response,
},
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the
/// ownership of `beneficiary`.
///
/// - `assets`: The asset(s) to be withdrawn.
/// - `beneficiary`: The new owner for the assets.
///
/// Safety: No concerns.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 4)]
TransferAsset { assets: MultiAssets, beneficiary: MultiLocation },
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the
/// ownership of `dest` within this consensus system (i.e. its sovereign account).
///
/// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`.
///
/// - `assets`: The asset(s) to be withdrawn.
/// - `dest`: The location whose sovereign account will own the assets and thus the effective beneficiary for the
/// assets and the notification target for the reserve asset deposit message.
/// - `effects`: The orders that should be contained in the `ReserveAssetDeposited` which is sent onwards to
/// `dest`.
///
/// Safety: No concerns.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 5)]
TransferReserveAsset { assets: MultiAssets, dest: MultiLocation, effects: Vec<Order<()>> },
/// Apply the encoded transaction `call`, whose dispatch-origin should be `origin` as expressed by the kind
/// of origin `origin_type`.
///
/// - `origin_type`: The means of expressing the message origin as a dispatch origin.
/// - `max_weight`: The weight of `call`; this should be at least the chain's calculated weight and will
/// be used in the weight determination arithmetic.
/// - `call`: The encoded transaction to be applied.
///
/// Safety: No concerns.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 6)]
Transact { origin_type: OriginKind, require_weight_at_most: u64, call: DoubleEncoded<Call> },
/// A message to notify about a new incoming HRMP channel. This message is meant to be sent by the
/// relay-chain to a para.
///
/// - `sender`: The sender in the to-be opened channel. Also, the initiator of the channel opening.
/// - `max_message_size`: The maximum size of a message proposed by the sender.
/// - `max_capacity`: The maximum number of messages that can be queued in the channel.
///
/// Safety: The message should originate directly from the relay-chain.
///
/// Kind: *System Notification*
#[codec(index = 7)]
HrmpNewChannelOpenRequest {
#[codec(compact)]
sender: u32,
#[codec(compact)]
max_message_size: u32,
#[codec(compact)]
max_capacity: u32,
},
/// A message to notify about that a previously sent open channel request has been accepted by
/// the recipient. That means that the channel will be opened during the next relay-chain session
/// change. This message is meant to be sent by the relay-chain to a para.
///
/// Safety: The message should originate directly from the relay-chain.
///
/// Kind: *System Notification*
///
/// Errors:
#[codec(index = 8)]
HrmpChannelAccepted {
#[codec(compact)]
recipient: u32,
},
/// A message to notify that the other party in an open channel decided to close it. In particular,
/// `initiator` is going to close the channel opened from `sender` to the `recipient`. The close
/// will be enacted at the next relay-chain session change. This message is meant to be sent by
/// the relay-chain to a para.
///
/// Safety: The message should originate directly from the relay-chain.
///
/// Kind: *System Notification*
///
/// Errors:
#[codec(index = 9)]
HrmpChannelClosing {
#[codec(compact)]
initiator: u32,
#[codec(compact)]
sender: u32,
#[codec(compact)]
recipient: u32,
},
/// A message to indicate that the embedded XCM is actually arriving on behalf of some consensus
/// location within the origin.
///
/// Safety: `who` must be an interior location of the context. This basically means that no `Parent`
/// junctions are allowed in it. This should be verified at the time of XCM execution.
///
/// Kind: *Instruction*
///
/// Errors:
#[codec(index = 10)]
RelayedFrom { who: MultiLocation, message: alloc::boxed::Box<Xcm<Call>> },
}
impl<Call> Xcm<Call> {
pub fn into<C>(self) -> Xcm<C> {
Xcm::from(self)
}
pub fn from<C>(xcm: Xcm<C>) -> Self {
use Xcm::*;
match xcm {
WithdrawAsset { assets, effects } =>
WithdrawAsset { assets, effects: effects.into_iter().map(Order::into).collect() },
ReserveAssetDeposited { assets, effects } => ReserveAssetDeposited {
assets,
effects: effects.into_iter().map(Order::into).collect(),
},
ReceiveTeleportedAsset { assets, effects } => ReceiveTeleportedAsset {
assets,
effects: effects.into_iter().map(Order::into).collect(),
},
QueryResponse { query_id: u64, response } => QueryResponse { query_id: u64, response },
TransferAsset { assets, beneficiary } => TransferAsset { assets, beneficiary },
TransferReserveAsset { assets, dest, effects } =>
TransferReserveAsset { assets, dest, effects },
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
HrmpChannelClosing { initiator, sender, recipient } =>
HrmpChannelClosing { initiator, sender, recipient },
Transact { origin_type, require_weight_at_most, call } =>
Transact { origin_type, require_weight_at_most, call: call.into() },
RelayedFrom { who, message } =>
RelayedFrom { who, message: alloc::boxed::Box::new((*message).into()) },
}
}
}
pub mod opaque {
/// The basic concrete type of `generic::Xcm`, which doesn't make any assumptions about the format of a
/// call other than it is pre-encoded.
pub type Xcm = super::Xcm<()>;
pub use super::order::opaque::*;
}
impl<Call> TryFrom<Xcm0<Call>> for Xcm<Call> {
type Error = ();
fn try_from(old: Xcm0<Call>) -> result::Result<Xcm<Call>, ()> {
use Xcm::*;
Ok(match old {
Xcm0::WithdrawAsset { assets, effects } => WithdrawAsset {
assets: assets.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm0::ReserveAssetDeposit { assets, effects } => ReserveAssetDeposited {
assets: assets.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm0::TeleportAsset { assets, effects } => ReceiveTeleportedAsset {
assets: assets.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm0::QueryResponse { query_id: u64, response } =>
QueryResponse { query_id: u64, response },
Xcm0::TransferAsset { assets, dest } =>
TransferAsset { assets: assets.try_into()?, beneficiary: dest.into() },
Xcm0::TransferReserveAsset { assets, dest, effects } => TransferReserveAsset {
assets: assets.try_into()?,
dest: dest.into(),
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm0::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
Xcm0::HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
Xcm0::HrmpChannelClosing { initiator, sender, recipient } =>
HrmpChannelClosing { initiator, sender, recipient },
Xcm0::Transact { origin_type, require_weight_at_most, call } =>
Transact { origin_type, require_weight_at_most, call: call.into() },
Xcm0::RelayedFrom { who, message } => RelayedFrom {
who: who.into(),
message: alloc::boxed::Box::new((*message).try_into()?),
},
})
}
}
+575
View File
@@ -0,0 +1,575 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format asset data structures.
//!
//! This encompasses four types for representing assets:
//! - `MultiAsset`: A description of a single asset, either an instance of a non-fungible or some amount of a fungible.
//! - `MultiAssets`: A collection of `MultiAsset`s. These are stored in a `Vec` and sorted with fungibles first.
//! - `Wild`: A single asset wildcard, this can either be "all" assets, or all assets of a specific kind.
//! - `MultiAssetFilter`: A combination of `Wild` and `MultiAssets` designed for efficiently filtering an XCM holding
//! account.
use super::{
Junction,
MultiLocation::{self, X1},
};
use alloc::{vec, vec::Vec};
use core::{
cmp::Ordering,
convert::{TryFrom, TryInto},
result,
};
use parity_scale_codec::{self as codec, Decode, Encode};
/// A general identifier for an instance of a non-fungible asset class.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)]
pub enum AssetInstance {
/// Undefined - used if the non-fungible asset class has only one instance.
Undefined,
/// A compact index. Technically this could be greater than `u128`, but this implementation supports only
/// values up to `2**128 - 1`.
Index(#[codec(compact)] u128),
/// A 4-byte fixed-length datum.
Array4([u8; 4]),
/// An 8-byte fixed-length datum.
Array8([u8; 8]),
/// A 16-byte fixed-length datum.
Array16([u8; 16]),
/// A 32-byte fixed-length datum.
Array32([u8; 32]),
/// An arbitrary piece of data. Use only when necessary.
Blob(Vec<u8>),
}
impl From<()> for AssetInstance {
fn from(_: ()) -> Self {
Self::Undefined
}
}
impl From<[u8; 4]> for AssetInstance {
fn from(x: [u8; 4]) -> Self {
Self::Array4(x)
}
}
impl From<[u8; 8]> for AssetInstance {
fn from(x: [u8; 8]) -> Self {
Self::Array8(x)
}
}
impl From<[u8; 16]> for AssetInstance {
fn from(x: [u8; 16]) -> Self {
Self::Array16(x)
}
}
impl From<[u8; 32]> for AssetInstance {
fn from(x: [u8; 32]) -> Self {
Self::Array32(x)
}
}
impl From<Vec<u8>> for AssetInstance {
fn from(x: Vec<u8>) -> Self {
Self::Blob(x)
}
}
/// Classification of an asset being concrete or abstract.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)]
pub enum AssetId {
Concrete(MultiLocation),
Abstract(Vec<u8>),
}
impl From<MultiLocation> for AssetId {
fn from(x: MultiLocation) -> Self {
Self::Concrete(x)
}
}
impl From<Junction> for AssetId {
fn from(x: Junction) -> Self {
Self::Concrete(X1(x))
}
}
impl From<Vec<u8>> for AssetId {
fn from(x: Vec<u8>) -> Self {
Self::Abstract(x)
}
}
impl AssetId {
/// Prepend a `MultiLocation` to a concrete asset, giving it a new root location.
pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
if let AssetId::Concrete(ref mut l) = self {
l.prepend_with(prepend.clone()).map_err(|_| ())?;
}
Ok(())
}
/// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `MultiAsset` value.
pub fn into_multiasset(self, fun: Fungibility) -> MultiAsset {
MultiAsset { fun, id: self }
}
/// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `WildMultiAsset`
/// wildcard (`AllOf`) value.
pub fn into_wild(self, fun: WildFungibility) -> WildMultiAsset {
WildMultiAsset::AllOf { fun, id: self }
}
}
/// Classification of whether an asset is fungible or not, along with a mandatory amount or instance.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)]
pub enum Fungibility {
Fungible(u128),
NonFungible(AssetInstance),
}
impl Fungibility {
pub fn is_kind(&self, w: WildFungibility) -> bool {
use Fungibility::*;
use WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible};
matches!((self, w), (Fungible(_), WildFungible) | (NonFungible(_), WildNonFungible))
}
}
impl From<u128> for Fungibility {
fn from(amount: u128) -> Fungibility {
debug_assert_ne!(amount, 0);
Fungibility::Fungible(amount)
}
}
impl<T: Into<AssetInstance>> From<T> for Fungibility {
fn from(instance: T) -> Fungibility {
Fungibility::NonFungible(instance.into())
}
}
#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode)]
pub struct MultiAsset {
pub id: AssetId,
pub fun: Fungibility,
}
impl PartialOrd for MultiAsset {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for MultiAsset {
fn cmp(&self, other: &Self) -> Ordering {
match (&self.fun, &other.fun) {
(Fungibility::Fungible(..), Fungibility::NonFungible(..)) => Ordering::Less,
(Fungibility::NonFungible(..), Fungibility::Fungible(..)) => Ordering::Greater,
_ => (&self.id, &self.fun).cmp(&(&other.id, &other.fun)),
}
}
}
impl<A: Into<AssetId>, B: Into<Fungibility>> From<(A, B)> for MultiAsset {
fn from((id, fun): (A, B)) -> MultiAsset {
MultiAsset { fun: fun.into(), id: id.into() }
}
}
impl MultiAsset {
pub fn is_fungible(&self, maybe_id: Option<AssetId>) -> bool {
use Fungibility::*;
matches!(self.fun, Fungible(..)) && maybe_id.map_or(true, |i| i == self.id)
}
pub fn is_non_fungible(&self, maybe_id: Option<AssetId>) -> bool {
use Fungibility::*;
matches!(self.fun, NonFungible(..)) && maybe_id.map_or(true, |i| i == self.id)
}
/// Prepend a `MultiLocation` to a concrete asset, giving it a new root location.
pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
self.id.reanchor(prepend)
}
/// Prepend a `MultiLocation` to a concrete asset, giving it a new root location.
pub fn reanchored(mut self, prepend: &MultiLocation) -> Result<Self, ()> {
self.reanchor(prepend)?;
Ok(self)
}
/// Returns true if `self` is a super-set of the given `inner`.
pub fn contains(&self, inner: &MultiAsset) -> bool {
use Fungibility::*;
if self.id == inner.id {
match (&self.fun, &inner.fun) {
(Fungible(a), Fungible(i)) if a >= i => return true,
(NonFungible(a), NonFungible(i)) if a == i => return true,
_ => (),
}
}
false
}
}
impl TryFrom<super::super::v0::MultiAsset> for MultiAsset {
type Error = ();
fn try_from(old: super::super::v0::MultiAsset) -> result::Result<MultiAsset, ()> {
use super::super::v0::MultiAsset as V0;
use AssetId::*;
use Fungibility::*;
let (id, fun) = match old {
V0::ConcreteFungible { id, amount } => (Concrete(id.into()), Fungible(amount)),
V0::ConcreteNonFungible { class, instance } =>
(Concrete(class.into()), NonFungible(instance)),
V0::AbstractFungible { id, amount } => (Abstract(id), Fungible(amount)),
V0::AbstractNonFungible { class, instance } => (Abstract(class), NonFungible(instance)),
_ => return Err(()),
};
Ok(MultiAsset { id, fun })
}
}
impl TryFrom<super::super::v0::MultiAsset> for Option<MultiAsset> {
type Error = ();
fn try_from(old: super::super::v0::MultiAsset) -> result::Result<Option<MultiAsset>, ()> {
match old {
super::super::v0::MultiAsset::None => return Ok(None),
x => return Ok(Some(x.try_into()?)),
}
}
}
impl TryFrom<Vec<super::super::v0::MultiAsset>> for MultiAsset {
type Error = ();
fn try_from(mut old: Vec<super::super::v0::MultiAsset>) -> result::Result<MultiAsset, ()> {
if old.len() == 1 {
old.remove(0).try_into()
} else {
Err(())
}
}
}
/// A `Vec` of `MultiAsset`s. There may be no duplicate fungible items in here and when decoding, they must be sorted.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode)]
pub struct MultiAssets(Vec<MultiAsset>);
impl Decode for MultiAssets {
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, parity_scale_codec::Error> {
Self::from_sorted_and_deduplicated(Vec::<MultiAsset>::decode(input)?)
.map_err(|()| "Out of order".into())
}
}
impl TryFrom<Vec<super::super::v0::MultiAsset>> for MultiAssets {
type Error = ();
fn try_from(old: Vec<super::super::v0::MultiAsset>) -> result::Result<MultiAssets, ()> {
let v = old
.into_iter()
.map(Option::<MultiAsset>::try_from)
.filter_map(|x| x.transpose())
.collect::<result::Result<Vec<MultiAsset>, ()>>()?;
Ok(v.into())
}
}
impl From<Vec<MultiAsset>> for MultiAssets {
fn from(mut assets: Vec<MultiAsset>) -> Self {
let mut res = Vec::with_capacity(assets.len());
if !assets.is_empty() {
assets.sort();
let mut iter = assets.into_iter();
if let Some(first) = iter.next() {
let last = iter.fold(first, |a, b| -> MultiAsset {
match (a, b) {
(
MultiAsset { fun: Fungibility::Fungible(a_amount), id: a_id },
MultiAsset { fun: Fungibility::Fungible(b_amount), id: b_id },
) if a_id == b_id =>
MultiAsset { id: a_id, fun: Fungibility::Fungible(a_amount + b_amount) },
(
MultiAsset { fun: Fungibility::NonFungible(a_instance), id: a_id },
MultiAsset { fun: Fungibility::NonFungible(b_instance), id: b_id },
) if a_id == b_id && a_instance == b_instance =>
MultiAsset { fun: Fungibility::NonFungible(a_instance), id: a_id },
(to_push, to_remember) => {
res.push(to_push);
to_remember
},
}
});
res.push(last);
}
}
Self(res)
}
}
impl<T: Into<MultiAsset>> From<T> for MultiAssets {
fn from(x: T) -> Self {
Self(vec![x.into()])
}
}
impl MultiAssets {
/// A new (empty) value.
pub fn new() -> Self {
Self(Vec::new())
}
/// Create a new instance of `MultiAssets` from a `Vec<MultiAsset>` whose contents are sorted and
/// which contain no duplicates.
///
/// Returns `Ok` if the operation succeeds and `Err` if `r` is out of order or had duplicates. If you can't
/// guarantee that `r` is sorted and deduplicated, then use `From::<Vec<MultiAsset>>::from` which is infallible.
pub fn from_sorted_and_deduplicated(r: Vec<MultiAsset>) -> Result<Self, ()> {
if r.is_empty() {
return Ok(Self(Vec::new()))
}
r.iter().skip(1).try_fold(&r[0], |a, b| -> Result<&MultiAsset, ()> {
if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) {
Ok(b)
} else {
Err(())
}
})?;
Ok(Self(r))
}
/// Create a new instance of `MultiAssets` from a `Vec<MultiAsset>` whose contents are sorted and
/// which contain no duplicates.
///
/// In release mode, this skips any checks to ensure that `r` is correct, making it a negligible-cost operation.
/// Generally though you should avoid using it unless you have a strict proof that `r` is valid.
#[cfg(test)]
pub fn from_sorted_and_deduplicated_skip_checks(r: Vec<MultiAsset>) -> Self {
Self::from_sorted_and_deduplicated(r).expect("Invalid input r is not sorted/deduped")
}
/// Create a new instance of `MultiAssets` from a `Vec<MultiAsset>` whose contents are sorted and
/// which contain no duplicates.
///
/// In release mode, this skips any checks to ensure that `r` is correct, making it a negligible-cost operation.
/// Generally though you should avoid using it unless you have a strict proof that `r` is valid.
///
/// In test mode, this checks anyway and panics on fail.
#[cfg(not(test))]
pub fn from_sorted_and_deduplicated_skip_checks(r: Vec<MultiAsset>) -> Self {
Self(r)
}
/// Add some asset onto the list, saturating. This is quite a laborious operation since it maintains the ordering.
pub fn push(&mut self, a: MultiAsset) {
if let Fungibility::Fungible(ref amount) = a.fun {
for asset in self.0.iter_mut().filter(|x| x.id == a.id) {
if let Fungibility::Fungible(ref mut balance) = asset.fun {
*balance = balance.saturating_add(*amount);
return
}
}
}
self.0.push(a);
self.0.sort();
}
/// Returns `true` if this definitely represents no asset.
pub fn is_none(&self) -> bool {
self.0.is_empty()
}
/// Returns true if `self` is a super-set of the given `inner`.
pub fn contains(&self, inner: &MultiAsset) -> bool {
self.0.iter().any(|i| i.contains(inner))
}
/// Consume `self` and return the inner vec.
pub fn drain(self) -> Vec<MultiAsset> {
self.0
}
/// Return a reference to the inner vec.
pub fn inner(&self) -> &Vec<MultiAsset> {
&self.0
}
/// Return the number of distinct asset instances contained.
pub fn len(&self) -> usize {
self.0.len()
}
/// Prepend a `MultiLocation` to any concrete asset items, giving it a new root location.
pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
self.0.iter_mut().try_for_each(|i| i.reanchor(prepend))
}
/// Return a reference to an item at a specific index or `None` if it doesn't exist.
pub fn get(&self, index: usize) -> Option<&MultiAsset> {
self.0.get(index)
}
}
/// Classification of whether an asset is fungible or not.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)]
pub enum WildFungibility {
Fungible,
NonFungible,
}
/// A wildcard representing a set of assets.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)]
pub enum WildMultiAsset {
/// All assets in the holding register, up to `usize` individual assets (different instances of non-fungibles could
/// be separate assets).
All,
/// All assets in the holding register of a given fungibility and ID. If operating on non-fungibles, then a limit
/// is provided for the maximum amount of matching instances.
AllOf { id: AssetId, fun: WildFungibility },
}
impl TryFrom<super::super::v0::MultiAsset> for WildMultiAsset {
type Error = ();
fn try_from(old: super::super::v0::MultiAsset) -> result::Result<WildMultiAsset, ()> {
use super::super::v0::MultiAsset as V0;
use AssetId::*;
use WildFungibility::*;
let (id, fun) = match old {
V0::All => return Ok(WildMultiAsset::All),
V0::AllConcreteFungible { id } => (Concrete(id.into()), Fungible),
V0::AllConcreteNonFungible { class } => (Concrete(class.into()), NonFungible),
V0::AllAbstractFungible { id } => (Abstract(id), Fungible),
V0::AllAbstractNonFungible { class } => (Abstract(class), NonFungible),
_ => return Err(()),
};
Ok(WildMultiAsset::AllOf { id, fun })
}
}
impl TryFrom<Vec<super::super::v0::MultiAsset>> for WildMultiAsset {
type Error = ();
fn try_from(mut old: Vec<super::super::v0::MultiAsset>) -> result::Result<WildMultiAsset, ()> {
if old.len() == 1 {
old.remove(0).try_into()
} else {
Err(())
}
}
}
impl WildMultiAsset {
/// Returns true if `self` is a super-set of the given `inner`.
///
/// Typically, any wildcard is never contained in anything else, and a wildcard can contain any other non-wildcard.
/// For more details, see the implementation and tests.
pub fn contains(&self, inner: &MultiAsset) -> bool {
use WildMultiAsset::*;
match self {
AllOf { fun, id } => inner.fun.is_kind(*fun) && &inner.id == id,
All => true,
}
}
/// Prepend a `MultiLocation` to any concrete asset components, giving it a new root location.
pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
use WildMultiAsset::*;
match self {
AllOf { ref mut id, .. } => id.reanchor(prepend).map_err(|_| ()),
All => Ok(()),
}
}
}
impl<A: Into<AssetId>, B: Into<WildFungibility>> From<(A, B)> for WildMultiAsset {
fn from((id, fun): (A, B)) -> WildMultiAsset {
WildMultiAsset::AllOf { fun: fun.into(), id: id.into() }
}
}
/// `MultiAsset` collection, either `MultiAssets` or a single wildcard.
///
/// Note: Vectors of wildcards whose encoding is supported in XCM v0 are unsupported
/// in this implementation and will result in a decode error.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)]
pub enum MultiAssetFilter {
Definite(MultiAssets),
Wild(WildMultiAsset),
}
impl<T: Into<WildMultiAsset>> From<T> for MultiAssetFilter {
fn from(x: T) -> Self {
Self::Wild(x.into())
}
}
impl From<MultiAsset> for MultiAssetFilter {
fn from(x: MultiAsset) -> Self {
Self::Definite(vec![x].into())
}
}
impl From<Vec<MultiAsset>> for MultiAssetFilter {
fn from(x: Vec<MultiAsset>) -> Self {
Self::Definite(x.into())
}
}
impl From<MultiAssets> for MultiAssetFilter {
fn from(x: MultiAssets) -> Self {
Self::Definite(x)
}
}
impl MultiAssetFilter {
/// Returns true if `self` is a super-set of the given `inner`.
///
/// Typically, any wildcard is never contained in anything else, and a wildcard can contain any other non-wildcard.
/// For more details, see the implementation and tests.
pub fn contains(&self, inner: &MultiAsset) -> bool {
match self {
MultiAssetFilter::Definite(ref assets) => assets.contains(inner),
MultiAssetFilter::Wild(ref wild) => wild.contains(inner),
}
}
/// Prepend a `MultiLocation` to any concrete asset components, giving it a new root location.
pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
match self {
MultiAssetFilter::Definite(ref mut assets) => assets.reanchor(prepend),
MultiAssetFilter::Wild(ref mut wild) => wild.reanchor(prepend),
}
}
}
impl TryFrom<Vec<super::super::v0::MultiAsset>> for MultiAssetFilter {
type Error = ();
fn try_from(
mut old: Vec<super::super::v0::MultiAsset>,
) -> result::Result<MultiAssetFilter, ()> {
if old.len() == 1 && old[0].is_wildcard() {
Ok(MultiAssetFilter::Wild(old.remove(0).try_into()?))
} else {
Ok(MultiAssetFilter::Definite(old.try_into()?))
}
}
}
+821
View File
@@ -0,0 +1,821 @@
// Copyright 2020-2021 Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format data structures.
use super::{super::v0::MultiLocation as MultiLocation0, Junction};
use core::{mem, result};
use parity_scale_codec::{self, Decode, Encode};
/// A relative path between state-bearing consensus systems.
///
/// A location in a consensus system is defined as an *isolatable state machine* held within global consensus. The
/// location in question need not have a sophisticated consensus algorithm of its own; a single account within
/// Ethereum, for example, could be considered a location.
///
/// A very-much non-exhaustive list of types of location include:
/// - A (normal, layer-1) block chain, e.g. the Bitcoin mainnet or a parachain.
/// - A layer-0 super-chain, e.g. the Polkadot Relay chain.
/// - A layer-2 smart contract, e.g. an ERC-20 on Ethereum.
/// - A logical functional component of a chain, e.g. a single instance of a pallet on a Frame-based Substrate chain.
/// - An account.
///
/// A `MultiLocation` is a *relative identifier*, meaning that it can only be used to define the relative path
/// between two locations, and cannot generally be used to refer to a location universally. It is comprised of a
/// number of *junctions*, each morphing the previous location, either diving down into one of its internal locations,
/// called a *sub-consensus*, or going up into its parent location. Correct `MultiLocation` values must have all
/// `Parent` junctions as a prefix to all *sub-consensus* junctions.
///
/// This specific `MultiLocation` implementation uses a Rust `enum` in order to make pattern matching easier.
///
/// The `MultiLocation` value of `Here` simply refers to the interpreting consensus system.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)]
pub enum MultiLocation {
/// The interpreting consensus system.
Here,
/// A relative path comprising 1 junction.
X1(Junction),
/// A relative path comprising 2 junctions.
X2(Junction, Junction),
/// A relative path comprising 3 junctions.
X3(Junction, Junction, Junction),
/// A relative path comprising 4 junctions.
X4(Junction, Junction, Junction, Junction),
/// A relative path comprising 5 junctions.
X5(Junction, Junction, Junction, Junction, Junction),
/// A relative path comprising 6 junctions.
X6(Junction, Junction, Junction, Junction, Junction, Junction),
/// A relative path comprising 7 junctions.
X7(Junction, Junction, Junction, Junction, Junction, Junction, Junction),
/// A relative path comprising 8 junctions.
X8(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction),
}
/// Maximum number of junctions a `MultiLocation` can contain.
pub const MAX_MULTILOCATION_LENGTH: usize = 8;
impl From<Junction> for MultiLocation {
fn from(x: Junction) -> Self {
MultiLocation::X1(x)
}
}
impl From<()> for MultiLocation {
fn from(_: ()) -> Self {
MultiLocation::Here
}
}
impl From<(Junction,)> for MultiLocation {
fn from(x: (Junction,)) -> Self {
MultiLocation::X1(x.0)
}
}
impl From<(Junction, Junction)> for MultiLocation {
fn from(x: (Junction, Junction)) -> Self {
MultiLocation::X2(x.0, x.1)
}
}
impl From<(Junction, Junction, Junction)> for MultiLocation {
fn from(x: (Junction, Junction, Junction)) -> Self {
MultiLocation::X3(x.0, x.1, x.2)
}
}
impl From<(Junction, Junction, Junction, Junction)> for MultiLocation {
fn from(x: (Junction, Junction, Junction, Junction)) -> Self {
MultiLocation::X4(x.0, x.1, x.2, x.3)
}
}
impl From<(Junction, Junction, Junction, Junction, Junction)> for MultiLocation {
fn from(x: (Junction, Junction, Junction, Junction, Junction)) -> Self {
MultiLocation::X5(x.0, x.1, x.2, x.3, x.4)
}
}
impl From<(Junction, Junction, Junction, Junction, Junction, Junction)> for MultiLocation {
fn from(x: (Junction, Junction, Junction, Junction, Junction, Junction)) -> Self {
MultiLocation::X6(x.0, x.1, x.2, x.3, x.4, x.5)
}
}
impl From<(Junction, Junction, Junction, Junction, Junction, Junction, Junction)>
for MultiLocation
{
fn from(x: (Junction, Junction, Junction, Junction, Junction, Junction, Junction)) -> Self {
MultiLocation::X7(x.0, x.1, x.2, x.3, x.4, x.5, x.6)
}
}
impl From<(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction)>
for MultiLocation
{
fn from(
x: (Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction),
) -> Self {
MultiLocation::X8(x.0, x.1, x.2, x.3, x.4, x.5, x.6, x.7)
}
}
impl From<[Junction; 0]> for MultiLocation {
fn from(_: [Junction; 0]) -> Self {
MultiLocation::Here
}
}
impl From<[Junction; 1]> for MultiLocation {
fn from(x: [Junction; 1]) -> Self {
let [x0] = x;
MultiLocation::X1(x0)
}
}
impl From<[Junction; 2]> for MultiLocation {
fn from(x: [Junction; 2]) -> Self {
let [x0, x1] = x;
MultiLocation::X2(x0, x1)
}
}
impl From<[Junction; 3]> for MultiLocation {
fn from(x: [Junction; 3]) -> Self {
let [x0, x1, x2] = x;
MultiLocation::X3(x0, x1, x2)
}
}
impl From<[Junction; 4]> for MultiLocation {
fn from(x: [Junction; 4]) -> Self {
let [x0, x1, x2, x3] = x;
MultiLocation::X4(x0, x1, x2, x3)
}
}
impl From<[Junction; 5]> for MultiLocation {
fn from(x: [Junction; 5]) -> Self {
let [x0, x1, x2, x3, x4] = x;
MultiLocation::X5(x0, x1, x2, x3, x4)
}
}
impl From<[Junction; 6]> for MultiLocation {
fn from(x: [Junction; 6]) -> Self {
let [x0, x1, x2, x3, x4, x5] = x;
MultiLocation::X6(x0, x1, x2, x3, x4, x5)
}
}
impl From<[Junction; 7]> for MultiLocation {
fn from(x: [Junction; 7]) -> Self {
let [x0, x1, x2, x3, x4, x5, x6] = x;
MultiLocation::X7(x0, x1, x2, x3, x4, x5, x6)
}
}
impl From<[Junction; 8]> for MultiLocation {
fn from(x: [Junction; 8]) -> Self {
let [x0, x1, x2, x3, x4, x5, x6, x7] = x;
MultiLocation::X8(x0, x1, x2, x3, x4, x5, x6, x7)
}
}
pub struct MultiLocationIterator(MultiLocation);
impl Iterator for MultiLocationIterator {
type Item = Junction;
fn next(&mut self) -> Option<Junction> {
self.0.take_first()
}
}
pub struct MultiLocationReverseIterator(MultiLocation);
impl Iterator for MultiLocationReverseIterator {
type Item = Junction;
fn next(&mut self) -> Option<Junction> {
self.0.take_last()
}
}
pub struct MultiLocationRefIterator<'a>(&'a MultiLocation, usize);
impl<'a> Iterator for MultiLocationRefIterator<'a> {
type Item = &'a Junction;
fn next(&mut self) -> Option<&'a Junction> {
let result = self.0.at(self.1);
self.1 += 1;
result
}
}
pub struct MultiLocationReverseRefIterator<'a>(&'a MultiLocation, usize);
impl<'a> Iterator for MultiLocationReverseRefIterator<'a> {
type Item = &'a Junction;
fn next(&mut self) -> Option<&'a Junction> {
self.1 += 1;
self.0.at(self.0.len().checked_sub(self.1)?)
}
}
impl MultiLocation {
/// Returns first junction, or `None` if the location is empty.
pub fn first(&self) -> Option<&Junction> {
match &self {
MultiLocation::Here => None,
MultiLocation::X1(ref a) => Some(a),
MultiLocation::X2(ref a, ..) => Some(a),
MultiLocation::X3(ref a, ..) => Some(a),
MultiLocation::X4(ref a, ..) => Some(a),
MultiLocation::X5(ref a, ..) => Some(a),
MultiLocation::X6(ref a, ..) => Some(a),
MultiLocation::X7(ref a, ..) => Some(a),
MultiLocation::X8(ref a, ..) => Some(a),
}
}
/// Returns last junction, or `None` if the location is empty.
pub fn last(&self) -> Option<&Junction> {
match &self {
MultiLocation::Here => None,
MultiLocation::X1(ref a) => Some(a),
MultiLocation::X2(.., ref a) => Some(a),
MultiLocation::X3(.., ref a) => Some(a),
MultiLocation::X4(.., ref a) => Some(a),
MultiLocation::X5(.., ref a) => Some(a),
MultiLocation::X6(.., ref a) => Some(a),
MultiLocation::X7(.., ref a) => Some(a),
MultiLocation::X8(.., ref a) => Some(a),
}
}
/// Splits off the first junction, returning the remaining suffix (first item in tuple) and the first element
/// (second item in tuple) or `None` if it was empty.
pub fn split_first(self) -> (MultiLocation, Option<Junction>) {
match self {
MultiLocation::Here => (MultiLocation::Here, None),
MultiLocation::X1(a) => (MultiLocation::Here, Some(a)),
MultiLocation::X2(a, b) => (MultiLocation::X1(b), Some(a)),
MultiLocation::X3(a, b, c) => (MultiLocation::X2(b, c), Some(a)),
MultiLocation::X4(a, b, c, d) => (MultiLocation::X3(b, c, d), Some(a)),
MultiLocation::X5(a, b, c, d, e) => (MultiLocation::X4(b, c, d, e), Some(a)),
MultiLocation::X6(a, b, c, d, e, f) => (MultiLocation::X5(b, c, d, e, f), Some(a)),
MultiLocation::X7(a, b, c, d, e, f, g) =>
(MultiLocation::X6(b, c, d, e, f, g), Some(a)),
MultiLocation::X8(a, b, c, d, e, f, g, h) =>
(MultiLocation::X7(b, c, d, e, f, g, h), Some(a)),
}
}
/// Splits off the last junction, returning the remaining prefix (first item in tuple) and the last element
/// (second item in tuple) or `None` if it was empty.
pub fn split_last(self) -> (MultiLocation, Option<Junction>) {
match self {
MultiLocation::Here => (MultiLocation::Here, None),
MultiLocation::X1(a) => (MultiLocation::Here, Some(a)),
MultiLocation::X2(a, b) => (MultiLocation::X1(a), Some(b)),
MultiLocation::X3(a, b, c) => (MultiLocation::X2(a, b), Some(c)),
MultiLocation::X4(a, b, c, d) => (MultiLocation::X3(a, b, c), Some(d)),
MultiLocation::X5(a, b, c, d, e) => (MultiLocation::X4(a, b, c, d), Some(e)),
MultiLocation::X6(a, b, c, d, e, f) => (MultiLocation::X5(a, b, c, d, e), Some(f)),
MultiLocation::X7(a, b, c, d, e, f, g) =>
(MultiLocation::X6(a, b, c, d, e, f), Some(g)),
MultiLocation::X8(a, b, c, d, e, f, g, h) =>
(MultiLocation::X7(a, b, c, d, e, f, g), Some(h)),
}
}
/// Removes the first element from `self`, returning it (or `None` if it was empty).
pub fn take_first(&mut self) -> Option<Junction> {
let mut d = MultiLocation::Here;
mem::swap(&mut *self, &mut d);
let (tail, head) = d.split_first();
*self = tail;
head
}
/// Removes the last element from `self`, returning it (or `None` if it was empty).
pub fn take_last(&mut self) -> Option<Junction> {
let mut d = MultiLocation::Here;
mem::swap(&mut *self, &mut d);
let (head, tail) = d.split_last();
*self = head;
tail
}
/// Consumes `self` and returns a `MultiLocation` suffixed with `new`, or an `Err` with the original value of
/// `self` in case of overflow.
pub fn pushed_with(self, new: Junction) -> result::Result<Self, Self> {
Ok(match self {
MultiLocation::Here => MultiLocation::X1(new),
MultiLocation::X1(a) => MultiLocation::X2(a, new),
MultiLocation::X2(a, b) => MultiLocation::X3(a, b, new),
MultiLocation::X3(a, b, c) => MultiLocation::X4(a, b, c, new),
MultiLocation::X4(a, b, c, d) => MultiLocation::X5(a, b, c, d, new),
MultiLocation::X5(a, b, c, d, e) => MultiLocation::X6(a, b, c, d, e, new),
MultiLocation::X6(a, b, c, d, e, f) => MultiLocation::X7(a, b, c, d, e, f, new),
MultiLocation::X7(a, b, c, d, e, f, g) => MultiLocation::X8(a, b, c, d, e, f, g, new),
s => Err(s)?,
})
}
/// Consumes `self` and returns a `MultiLocation` prefixed with `new`, or an `Err` with the original value of
/// `self` in case of overflow.
pub fn pushed_front_with(self, new: Junction) -> result::Result<Self, Self> {
Ok(match self {
MultiLocation::Here => MultiLocation::X1(new),
MultiLocation::X1(a) => MultiLocation::X2(new, a),
MultiLocation::X2(a, b) => MultiLocation::X3(new, a, b),
MultiLocation::X3(a, b, c) => MultiLocation::X4(new, a, b, c),
MultiLocation::X4(a, b, c, d) => MultiLocation::X5(new, a, b, c, d),
MultiLocation::X5(a, b, c, d, e) => MultiLocation::X6(new, a, b, c, d, e),
MultiLocation::X6(a, b, c, d, e, f) => MultiLocation::X7(new, a, b, c, d, e, f),
MultiLocation::X7(a, b, c, d, e, f, g) => MultiLocation::X8(new, a, b, c, d, e, f, g),
s => Err(s)?,
})
}
/// Returns the number of junctions in `self`.
pub fn len(&self) -> usize {
match &self {
MultiLocation::Here => 0,
MultiLocation::X1(..) => 1,
MultiLocation::X2(..) => 2,
MultiLocation::X3(..) => 3,
MultiLocation::X4(..) => 4,
MultiLocation::X5(..) => 5,
MultiLocation::X6(..) => 6,
MultiLocation::X7(..) => 7,
MultiLocation::X8(..) => 8,
}
}
/// Returns the junction at index `i`, or `None` if the location doesn't contain that many elements.
pub fn at(&self, i: usize) -> Option<&Junction> {
Some(match (i, &self) {
(0, MultiLocation::X1(ref a)) => a,
(0, MultiLocation::X2(ref a, ..)) => a,
(0, MultiLocation::X3(ref a, ..)) => a,
(0, MultiLocation::X4(ref a, ..)) => a,
(0, MultiLocation::X5(ref a, ..)) => a,
(0, MultiLocation::X6(ref a, ..)) => a,
(0, MultiLocation::X7(ref a, ..)) => a,
(0, MultiLocation::X8(ref a, ..)) => a,
(1, MultiLocation::X2(_, ref a)) => a,
(1, MultiLocation::X3(_, ref a, ..)) => a,
(1, MultiLocation::X4(_, ref a, ..)) => a,
(1, MultiLocation::X5(_, ref a, ..)) => a,
(1, MultiLocation::X6(_, ref a, ..)) => a,
(1, MultiLocation::X7(_, ref a, ..)) => a,
(1, MultiLocation::X8(_, ref a, ..)) => a,
(2, MultiLocation::X3(_, _, ref a)) => a,
(2, MultiLocation::X4(_, _, ref a, ..)) => a,
(2, MultiLocation::X5(_, _, ref a, ..)) => a,
(2, MultiLocation::X6(_, _, ref a, ..)) => a,
(2, MultiLocation::X7(_, _, ref a, ..)) => a,
(2, MultiLocation::X8(_, _, ref a, ..)) => a,
(3, MultiLocation::X4(_, _, _, ref a)) => a,
(3, MultiLocation::X5(_, _, _, ref a, ..)) => a,
(3, MultiLocation::X6(_, _, _, ref a, ..)) => a,
(3, MultiLocation::X7(_, _, _, ref a, ..)) => a,
(3, MultiLocation::X8(_, _, _, ref a, ..)) => a,
(4, MultiLocation::X5(_, _, _, _, ref a)) => a,
(4, MultiLocation::X6(_, _, _, _, ref a, ..)) => a,
(4, MultiLocation::X7(_, _, _, _, ref a, ..)) => a,
(4, MultiLocation::X8(_, _, _, _, ref a, ..)) => a,
(5, MultiLocation::X6(_, _, _, _, _, ref a)) => a,
(5, MultiLocation::X7(_, _, _, _, _, ref a, ..)) => a,
(5, MultiLocation::X8(_, _, _, _, _, ref a, ..)) => a,
(6, MultiLocation::X7(_, _, _, _, _, _, ref a)) => a,
(6, MultiLocation::X8(_, _, _, _, _, _, ref a, ..)) => a,
(7, MultiLocation::X8(_, _, _, _, _, _, _, ref a)) => a,
_ => return None,
})
}
/// Returns a mutable reference to the junction at index `i`, or `None` if the location doesn't contain that many
/// elements.
pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> {
Some(match (i, self) {
(0, MultiLocation::X1(ref mut a)) => a,
(0, MultiLocation::X2(ref mut a, ..)) => a,
(0, MultiLocation::X3(ref mut a, ..)) => a,
(0, MultiLocation::X4(ref mut a, ..)) => a,
(0, MultiLocation::X5(ref mut a, ..)) => a,
(0, MultiLocation::X6(ref mut a, ..)) => a,
(0, MultiLocation::X7(ref mut a, ..)) => a,
(0, MultiLocation::X8(ref mut a, ..)) => a,
(1, MultiLocation::X2(_, ref mut a)) => a,
(1, MultiLocation::X3(_, ref mut a, ..)) => a,
(1, MultiLocation::X4(_, ref mut a, ..)) => a,
(1, MultiLocation::X5(_, ref mut a, ..)) => a,
(1, MultiLocation::X6(_, ref mut a, ..)) => a,
(1, MultiLocation::X7(_, ref mut a, ..)) => a,
(1, MultiLocation::X8(_, ref mut a, ..)) => a,
(2, MultiLocation::X3(_, _, ref mut a)) => a,
(2, MultiLocation::X4(_, _, ref mut a, ..)) => a,
(2, MultiLocation::X5(_, _, ref mut a, ..)) => a,
(2, MultiLocation::X6(_, _, ref mut a, ..)) => a,
(2, MultiLocation::X7(_, _, ref mut a, ..)) => a,
(2, MultiLocation::X8(_, _, ref mut a, ..)) => a,
(3, MultiLocation::X4(_, _, _, ref mut a)) => a,
(3, MultiLocation::X5(_, _, _, ref mut a, ..)) => a,
(3, MultiLocation::X6(_, _, _, ref mut a, ..)) => a,
(3, MultiLocation::X7(_, _, _, ref mut a, ..)) => a,
(3, MultiLocation::X8(_, _, _, ref mut a, ..)) => a,
(4, MultiLocation::X5(_, _, _, _, ref mut a)) => a,
(4, MultiLocation::X6(_, _, _, _, ref mut a, ..)) => a,
(4, MultiLocation::X7(_, _, _, _, ref mut a, ..)) => a,
(4, MultiLocation::X8(_, _, _, _, ref mut a, ..)) => a,
(5, MultiLocation::X6(_, _, _, _, _, ref mut a)) => a,
(5, MultiLocation::X7(_, _, _, _, _, ref mut a, ..)) => a,
(5, MultiLocation::X8(_, _, _, _, _, ref mut a, ..)) => a,
(6, MultiLocation::X7(_, _, _, _, _, _, ref mut a)) => a,
(6, MultiLocation::X8(_, _, _, _, _, _, ref mut a, ..)) => a,
(7, MultiLocation::X8(_, _, _, _, _, _, _, ref mut a)) => a,
_ => return None,
})
}
/// Returns a reference iterator over the junctions.
pub fn iter(&self) -> MultiLocationRefIterator {
MultiLocationRefIterator(&self, 0)
}
/// Returns a reference iterator over the junctions in reverse.
pub fn iter_rev(&self) -> MultiLocationReverseRefIterator {
MultiLocationReverseRefIterator(&self, 0)
}
/// Consumes `self` and returns an iterator over the junctions.
pub fn into_iter(self) -> MultiLocationIterator {
MultiLocationIterator(self)
}
/// Consumes `self` and returns an iterator over the junctions in reverse.
pub fn into_iter_rev(self) -> MultiLocationReverseIterator {
MultiLocationReverseIterator(self)
}
/// Ensures that self begins with `prefix` and that it has a single `Junction` item following.
/// If so, returns a reference to this `Junction` item.
///
/// # Example
/// ```rust
/// # use xcm::latest::{MultiLocation::*, Junction::*};
/// # fn main() {
/// let mut m = X3(Parent, PalletInstance(3), OnlyChild);
/// assert_eq!(m.match_and_split(&X2(Parent, PalletInstance(3))), Some(&OnlyChild));
/// assert_eq!(m.match_and_split(&X1(Parent)), None);
/// # }
/// ```
pub fn match_and_split(&self, prefix: &MultiLocation) -> Option<&Junction> {
if prefix.len() + 1 != self.len() {
return None
}
for i in 0..prefix.len() {
if prefix.at(i) != self.at(i) {
return None
}
}
return self.at(prefix.len())
}
/// Mutates `self`, suffixing it with `new`. Returns `Err` in case of overflow.
pub fn push(&mut self, new: Junction) -> result::Result<(), ()> {
let mut n = MultiLocation::Here;
mem::swap(&mut *self, &mut n);
match n.pushed_with(new) {
Ok(result) => {
*self = result;
Ok(())
},
Err(old) => {
*self = old;
Err(())
},
}
}
/// Mutates `self`, prefixing it with `new`. Returns `Err` in case of overflow.
pub fn push_front(&mut self, new: Junction) -> result::Result<(), ()> {
let mut n = MultiLocation::Here;
mem::swap(&mut *self, &mut n);
match n.pushed_front_with(new) {
Ok(result) => {
*self = result;
Ok(())
},
Err(old) => {
*self = old;
Err(())
},
}
}
/// Returns the number of `Parent` junctions at the beginning of `self`.
pub fn leading_parent_count(&self) -> usize {
use Junction::Parent;
match self {
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent) => 8,
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, ..) => 7,
MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent) => 7,
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, ..) => 6,
MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, Parent, ..) => 6,
MultiLocation::X6(Parent, Parent, Parent, Parent, Parent, Parent) => 6,
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, ..) => 5,
MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, ..) => 5,
MultiLocation::X6(Parent, Parent, Parent, Parent, Parent, ..) => 5,
MultiLocation::X5(Parent, Parent, Parent, Parent, Parent) => 5,
MultiLocation::X8(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X7(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X6(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X5(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X4(Parent, Parent, Parent, Parent) => 4,
MultiLocation::X8(Parent, Parent, Parent, ..) => 3,
MultiLocation::X7(Parent, Parent, Parent, ..) => 3,
MultiLocation::X6(Parent, Parent, Parent, ..) => 3,
MultiLocation::X5(Parent, Parent, Parent, ..) => 3,
MultiLocation::X4(Parent, Parent, Parent, ..) => 3,
MultiLocation::X3(Parent, Parent, Parent) => 3,
MultiLocation::X8(Parent, Parent, ..) => 2,
MultiLocation::X7(Parent, Parent, ..) => 2,
MultiLocation::X6(Parent, Parent, ..) => 2,
MultiLocation::X5(Parent, Parent, ..) => 2,
MultiLocation::X4(Parent, Parent, ..) => 2,
MultiLocation::X3(Parent, Parent, ..) => 2,
MultiLocation::X2(Parent, Parent) => 2,
MultiLocation::X8(Parent, ..) => 1,
MultiLocation::X7(Parent, ..) => 1,
MultiLocation::X6(Parent, ..) => 1,
MultiLocation::X5(Parent, ..) => 1,
MultiLocation::X4(Parent, ..) => 1,
MultiLocation::X3(Parent, ..) => 1,
MultiLocation::X2(Parent, ..) => 1,
MultiLocation::X1(Parent) => 1,
_ => 0,
}
}
/// This function ensures a multi-junction is in its canonicalized/normalized form, removing
/// any internal `[Non-Parent, Parent]` combinations.
pub fn canonicalize(&mut self) {
let mut normalized = MultiLocation::Here;
let mut iter = self.iter();
// We build up the the new normalized path by taking items from the original multi-location.
// When the next item we would add is `Parent`, we instead remove the last item assuming
// it is non-parent.
const EXPECT_MESSAGE: &'static str =
"`self` is a well formed multi-location with N junctions; \
this loop iterates over the junctions of `self`; \
the loop can push to the new multi-location at most one time; \
thus the size of the new multi-location is at most N junctions; \
qed";
while let Some(j) = iter.next() {
if j == &Junction::Parent {
match normalized.last() {
None | Some(Junction::Parent) => {},
Some(_) => {
normalized.take_last();
continue
},
}
}
normalized.push(j.clone()).expect(EXPECT_MESSAGE);
}
core::mem::swap(self, &mut normalized);
}
/// Mutate `self` so that it is suffixed with `suffix`. The correct normalized form is returned,
/// removing any internal `[Non-Parent, Parent]` combinations.
///
/// In the case of overflow, `self` is unmodified and we return `Err` with `suffix`.
///
/// # Example
/// ```rust
/// # use xcm::latest::{MultiLocation::*, Junction::*};
/// # fn main() {
/// let mut m = X3(Parent, Parachain(21), OnlyChild);
/// assert_eq!(m.append_with(X2(Parent, PalletInstance(3))), Ok(()));
/// assert_eq!(m, X3(Parent, Parachain(21), PalletInstance(3)));
/// # }
/// ```
pub fn append_with(&mut self, suffix: MultiLocation) -> Result<(), MultiLocation> {
let mut prefix = suffix;
core::mem::swap(self, &mut prefix);
match self.prepend_with(prefix) {
Ok(()) => Ok(()),
Err(prefix) => {
let mut suffix = prefix;
core::mem::swap(self, &mut suffix);
Err(suffix)
},
}
}
/// Mutate `self` so that it is prefixed with `prefix`. The correct normalized form is returned,
/// removing any internal [Non-Parent, `Parent`] combinations.
///
/// In the case of overflow, `self` is unmodified and we return `Err` with `prefix`.
///
/// # Example
/// ```rust
/// # use xcm::latest::{MultiLocation::*, Junction::*, NetworkId::Any};
/// # fn main() {
/// let mut m = X3(Parent, Parent, PalletInstance(3));
/// assert_eq!(m.prepend_with(X3(Parent, Parachain(21), OnlyChild)), Ok(()));
/// assert_eq!(m, X2(Parent, PalletInstance(3)));
/// # }
/// ```
pub fn prepend_with(&mut self, prefix: MultiLocation) -> Result<(), MultiLocation> {
let mut prefix = prefix;
// This will guarantee that all `Parent` junctions in the prefix are leading, which is
// important for calculating the `skipped` items below.
prefix.canonicalize();
let self_leading_parents = self.leading_parent_count();
// These are the number of `non-parent` items in the prefix that we can
// potentially remove if the original location leads with parents.
let prefix_rest = prefix.len() - prefix.leading_parent_count();
// 2 * skipped items will be removed when performing the normalization below.
let skipped = self_leading_parents.min(prefix_rest);
// Pre-pending this prefix would create a multi-location with too many junctions.
if self.len() + prefix.len() - 2 * skipped > MAX_MULTILOCATION_LENGTH {
return Err(prefix)
}
// Here we cancel out `[Non-Parent, Parent]` items (normalization), where
// the non-parent item comes from the end of the prefix, and the parent item
// comes from the front of the original location.
//
// We calculated already how many of these there should be above.
for _ in 0..skipped {
let _non_parent = prefix.take_last();
let _parent = self.take_first();
debug_assert!(
_non_parent.is_some() && _non_parent != Some(Junction::Parent),
"prepend_with should always remove a non-parent from the end of the prefix",
);
debug_assert!(
_parent == Some(Junction::Parent),
"prepend_with should always remove a parent from the front of the location",
);
}
for j in prefix.into_iter_rev() {
self.push_front(j)
.expect("len + prefix minus 2*skipped is less than max length; qed");
}
Ok(())
}
/// Returns true iff `self` is an interior location. For this it may not contain any `Junction`s
/// for which `Junction::is_interior` returns `false`. This is generally true, except for the
/// `Parent` item.
///
/// # Example
/// ```rust
/// # use xcm::latest::{MultiLocation::*, Junction::*, NetworkId::Any};
/// # fn main() {
/// let parent = X1(Parent);
/// assert_eq!(parent.is_interior(), false);
/// let m = X2(PalletInstance(12), AccountIndex64 { network: Any, index: 23 });
/// assert_eq!(m.is_interior(), true);
/// # }
/// ```
pub fn is_interior(&self) -> bool {
self.iter().all(Junction::is_interior)
}
}
impl From<MultiLocation0> for MultiLocation {
fn from(old: MultiLocation0) -> Self {
use MultiLocation::*;
match old {
MultiLocation0::Null => Here,
MultiLocation0::X1(j0) => X1(j0),
MultiLocation0::X2(j0, j1) => X2(j0, j1),
MultiLocation0::X3(j0, j1, j2) => X3(j0, j1, j2),
MultiLocation0::X4(j0, j1, j2, j3) => X4(j0, j1, j2, j3),
MultiLocation0::X5(j0, j1, j2, j3, j4) => X5(j0, j1, j2, j3, j4),
MultiLocation0::X6(j0, j1, j2, j3, j4, j5) => X6(j0, j1, j2, j3, j4, j5),
MultiLocation0::X7(j0, j1, j2, j3, j4, j5, j6) => X7(j0, j1, j2, j3, j4, j5, j6),
MultiLocation0::X8(j0, j1, j2, j3, j4, j5, j6, j7) =>
X8(j0, j1, j2, j3, j4, j5, j6, j7),
}
}
}
#[cfg(test)]
mod tests {
use super::MultiLocation::*;
use crate::opaque::v1::{Junction::*, NetworkId::Any};
#[test]
fn match_and_split_works() {
let m = X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 });
assert_eq!(m.match_and_split(&X1(Parent)), None);
assert_eq!(
m.match_and_split(&X2(Parent, Parachain(42))),
Some(&AccountIndex64 { network: Any, index: 23 })
);
assert_eq!(m.match_and_split(&m), None);
}
#[test]
fn append_with_works() {
let acc = AccountIndex64 { network: Any, index: 23 };
let mut m = X2(Parent, Parachain(42));
assert_eq!(m.append_with(X2(PalletInstance(3), acc.clone())), Ok(()));
assert_eq!(m, X4(Parent, Parachain(42), PalletInstance(3), acc.clone()));
// cannot append to create overly long multilocation
let acc = AccountIndex64 { network: Any, index: 23 };
let mut m = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parachain(42));
let suffix = X2(PalletInstance(3), acc.clone());
assert_eq!(m.append_with(suffix.clone()), Err(suffix));
}
#[test]
fn prepend_with_works() {
let mut m = X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 });
assert_eq!(m.prepend_with(X2(Parent, OnlyChild)), Ok(()));
assert_eq!(m, X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 }));
// cannot prepend to create overly long multilocation
let mut m = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parachain(42));
let prefix = X2(Parent, Parent);
assert_eq!(m.prepend_with(prefix.clone()), Err(prefix));
// Can handle shared prefix and resizing correctly.
let mut m = X1(Parent);
let prefix = X8(
Parachain(100),
OnlyChild,
OnlyChild,
OnlyChild,
OnlyChild,
OnlyChild,
OnlyChild,
Parent,
);
assert_eq!(m.prepend_with(prefix.clone()), Ok(()));
assert_eq!(m, X5(Parachain(100), OnlyChild, OnlyChild, OnlyChild, OnlyChild));
let mut m = X1(Parent);
let prefix = X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent);
assert_eq!(m.prepend_with(prefix.clone()), Err(prefix));
let mut m = X1(Parent);
let prefix = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent);
assert_eq!(m.prepend_with(prefix.clone()), Ok(()));
assert_eq!(m, X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent));
let mut m = X1(Parent);
let prefix = X8(Parent, Parent, Parent, Parent, OnlyChild, Parent, Parent, Parent);
assert_eq!(m.prepend_with(prefix.clone()), Ok(()));
assert_eq!(m, X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent));
}
#[test]
fn canonicalize_works() {
let mut m = X1(Parent);
m.canonicalize();
assert_eq!(m, X1(Parent));
let mut m = X1(Parachain(1));
m.canonicalize();
assert_eq!(m, X1(Parachain(1)));
let mut m = X6(Parent, Parachain(1), Parent, Parachain(2), Parent, Parachain(3));
m.canonicalize();
assert_eq!(m, X2(Parent, Parachain(3)));
let mut m = X5(Parachain(1), Parent, Parachain(2), Parent, Parachain(3));
m.canonicalize();
assert_eq!(m, X1(Parachain(3)));
let mut m = X6(Parachain(1), Parent, Parachain(2), Parent, Parachain(3), Parent);
m.canonicalize();
assert_eq!(m, Here);
let mut m = X5(Parachain(1), Parent, Parent, Parent, Parachain(3));
m.canonicalize();
assert_eq!(m, X3(Parent, Parent, Parachain(3)));
let mut m = X4(Parachain(1), Parachain(2), Parent, Parent);
m.canonicalize();
assert_eq!(m, Here);
let mut m = X4(Parent, Parent, Parachain(1), Parachain(2));
m.canonicalize();
assert_eq!(m, X4(Parent, Parent, Parachain(1), Parachain(2)));
}
}
+243
View File
@@ -0,0 +1,243 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
//! Version 1 of the Cross-Consensus Message format data structures.
use super::{
super::v0::Order as Order0, MultiAsset, MultiAssetFilter, MultiAssets, MultiLocation, Xcm,
};
use alloc::{vec, vec::Vec};
use core::{
convert::{TryFrom, TryInto},
result,
};
use derivative::Derivative;
use parity_scale_codec::{self, Decode, Encode};
/// An instruction to be executed on some or all of the assets in holding, used by asset-related XCM messages.
#[derive(Derivative, Encode, Decode)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
pub enum Order<Call> {
/// Do nothing. Not generally used.
#[codec(index = 0)]
Noop,
/// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `beneficiary`
/// within this consensus system.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. Only the first
/// `max_assets` assets/instances of those matched by `assets` will be removed, prioritized under standard asset
/// ordering. Any others will remain in holding.
/// - `beneficiary`: The new owner for the assets.
///
/// Errors:
#[codec(index = 1)]
DepositAsset { assets: MultiAssetFilter, max_assets: u32, beneficiary: MultiLocation },
/// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within
/// this consensus system (i.e. its sovereign account).
///
/// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. Only the first
/// `max_assets` assets/instances of those matched by `assets` will be removed, prioritized under standard asset
/// ordering. Any others will remain in holding.
/// - `dest`: The location whose sovereign account will own the assets and thus the effective beneficiary for the
/// assets and the notification target for the reserve asset deposit message.
/// - `effects`: The orders that should be contained in the `ReserveAssetDeposited` which is sent onwards to
/// `dest`.
///
/// Errors:
#[codec(index = 2)]
DepositReserveAsset {
assets: MultiAssetFilter,
max_assets: u32,
dest: MultiLocation,
effects: Vec<Order<()>>,
},
/// Remove the asset(s) (`give`) from holding and replace them with alternative assets.
///
/// The minimum amount of assets to be received into holding for the order not to fail may be stated.
///
/// - `give`: The asset(s) to remove from holding.
/// - `receive`: The minimum amount of assets(s) which `give` should be exchanged for.
///
/// Errors:
#[codec(index = 3)]
ExchangeAsset { give: MultiAssetFilter, receive: MultiAssets },
/// Remove the asset(s) (`assets`) from holding and send a `WithdrawAsset` XCM message to a reserve location.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `reserve`: A valid location that acts as a reserve for all asset(s) in `assets`. The sovereign account
/// of this consensus system *on the reserve location* will have appropriate assets withdrawn and `effects` will
/// be executed on them. There will typically be only one valid location on any given asset/chain combination.
/// - `effects`: The orders to execute on the assets once withdrawn *on the reserve location*.
///
/// Errors:
#[codec(index = 4)]
InitiateReserveWithdraw {
assets: MultiAssetFilter,
reserve: MultiLocation,
effects: Vec<Order<()>>,
},
/// Remove the asset(s) (`assets`) from holding and send a `ReceiveTeleportedAsset` XCM message to a `destination`
/// location.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `destination`: A valid location that has a bi-lateral teleportation arrangement.
/// - `effects`: The orders to execute on the assets once arrived *on the destination location*.
///
/// NOTE: The `destination` location *MUST* respect this origin as a valid teleportation origin for all `assets`.
/// If it does not, then the assets may be lost.
///
/// Errors:
#[codec(index = 5)]
InitiateTeleport { assets: MultiAssetFilter, dest: MultiLocation, effects: Vec<Order<()>> },
/// Send a `Balances` XCM message with the `assets` value equal to the holding contents, or a portion thereof.
///
/// - `query_id`: An identifier that will be replicated into the returned XCM message.
/// - `dest`: A valid destination for the returned XCM message. This may be limited to the current origin.
/// - `assets`: A filter for the assets that should be reported back. The assets reported back will be, asset-
/// wise, *the lesser of this value and the holding register*. No wildcards will be used when reporting assets
/// back.
///
/// Errors:
#[codec(index = 6)]
QueryHolding {
#[codec(compact)]
query_id: u64,
dest: MultiLocation,
assets: MultiAssetFilter,
},
/// Pay for the execution of some XCM `instructions` and `orders` with up to `weight` picoseconds of execution time,
/// paying for this with up to `fees` from the Holding Register.
///
/// - `fees`: The asset(s) to remove from holding to pay for fees.
/// - `weight`: The amount of weight to purchase; this should be at least the shallow weight of `effects` and `xcm`.
/// - `debt`: The amount of weight-debt already incurred to be paid off; this should be equal to the unpaid weight of
/// any surrounding operations/orders.
/// - `halt_on_error`: If `true`, the execution of the `orders` and `operations` will halt on the first failure. If
/// `false`, then execution will continue regardless.
/// - `orders`: Orders to be executed with the existing Holding Register; execution of these orders happens PRIOR to
/// execution of the `operations`. The (shallow) weight for these must be paid for with the `weight` purchased.
/// - `instructions`: XCM instructions to be executed outside of the context of the current Holding Register;
/// execution of these instructions happens AFTER the execution of the `orders`. The (shallow) weight for these
/// must be paid for with the `weight` purchased.
/// Errors:
#[codec(index = 7)]
BuyExecution {
fees: MultiAsset,
weight: u64,
debt: u64,
halt_on_error: bool,
orders: Vec<Order<Call>>,
instructions: Vec<Xcm<Call>>,
},
}
pub mod opaque {
pub type Order = super::Order<()>;
}
impl<Call> Order<Call> {
pub fn into<C>(self) -> Order<C> {
Order::from(self)
}
pub fn from<C>(order: Order<C>) -> Self {
use Order::*;
match order {
Noop => Noop,
DepositAsset { assets, max_assets, beneficiary } =>
DepositAsset { assets, max_assets, beneficiary },
DepositReserveAsset { assets, max_assets, dest, effects } =>
DepositReserveAsset { assets, max_assets, dest, effects },
ExchangeAsset { give, receive } => ExchangeAsset { give, receive },
InitiateReserveWithdraw { assets, reserve, effects } =>
InitiateReserveWithdraw { assets, reserve, effects },
InitiateTeleport { assets, dest, effects } =>
InitiateTeleport { assets, dest, effects },
QueryHolding { query_id, dest, assets } => QueryHolding { query_id, dest, assets },
BuyExecution { fees, weight, debt, halt_on_error, orders, instructions } => {
let orders = orders.into_iter().map(Order::from).collect();
let instructions = instructions.into_iter().map(Xcm::from).collect();
BuyExecution { fees, weight, debt, halt_on_error, orders, instructions }
},
}
}
}
impl<Call> TryFrom<Order0<Call>> for Order<Call> {
type Error = ();
fn try_from(old: Order0<Call>) -> result::Result<Order<Call>, ()> {
use Order::*;
Ok(match old {
Order0::Null => Noop,
Order0::DepositAsset { assets, dest } =>
DepositAsset { assets: assets.try_into()?, max_assets: 1, beneficiary: dest.into() },
Order0::DepositReserveAsset { assets, dest, effects } => DepositReserveAsset {
assets: assets.try_into()?,
max_assets: 1,
dest: dest.into(),
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Order0::ExchangeAsset { give, receive } =>
ExchangeAsset { give: give.try_into()?, receive: receive.try_into()? },
Order0::InitiateReserveWithdraw { assets, reserve, effects } =>
InitiateReserveWithdraw {
assets: assets.try_into()?,
reserve: reserve.into(),
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Order0::InitiateTeleport { assets, dest, effects } => InitiateTeleport {
assets: assets.try_into()?,
dest: dest.into(),
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Order0::QueryHolding { query_id, dest, assets } =>
QueryHolding { query_id, dest: dest.into(), assets: assets.try_into()? },
Order0::BuyExecution { fees, weight, debt, halt_on_error, xcm } => {
let instructions =
xcm.into_iter().map(Xcm::<Call>::try_from).collect::<result::Result<_, _>>()?;
BuyExecution {
fees: fees.try_into()?,
weight,
debt,
halt_on_error,
orders: vec![],
instructions,
}
},
})
}
}
+263
View File
@@ -0,0 +1,263 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format data structures.
use core::result;
use parity_scale_codec::{Decode, Encode};
use super::{MultiLocation, Xcm};
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug)]
pub enum Error {
Undefined,
/// An arithmetic overflow happened.
Overflow,
/// The operation is intentionally unsupported.
Unimplemented,
UnhandledXcmVersion,
/// The implementation does not handle a given XCM.
UnhandledXcmMessage,
/// The implementation does not handle an effect present in an XCM.
UnhandledEffect,
EscalationOfPrivilege,
UntrustedReserveLocation,
UntrustedTeleportLocation,
DestinationBufferOverflow,
/// The message and destination was recognized as being reachable but the operation could not be completed.
/// A human-readable explanation of the specific issue is provided.
SendFailed(#[codec(skip)] &'static str),
/// The message and destination combination was not recognized as being reachable.
CannotReachDestination(MultiLocation, Xcm<()>),
MultiLocationFull,
FailedToDecode,
BadOrigin,
ExceedsMaxMessageSize,
/// An asset transaction (like withdraw or deposit) failed.
/// See implementers of the `TransactAsset` trait for sources.
/// Causes can include type conversion failures between id or balance types.
FailedToTransactAsset(#[codec(skip)] &'static str),
/// Execution of the XCM would potentially result in a greater weight used than the pre-specified
/// weight limit. The amount that is potentially required is the parameter.
WeightLimitReached(Weight),
/// An asset wildcard was passed where it was not expected (e.g. as the asset to withdraw in a
/// `WithdrawAsset` XCM).
Wildcard,
/// The case where an XCM message has specified a optional weight limit and the weight required for
/// processing is too great.
///
/// Used by:
/// - `Transact`
TooMuchWeightRequired,
/// The fees specified by the XCM message were not found in the holding register.
///
/// Used by:
/// - `BuyExecution`
NotHoldingFees,
/// The weight of an XCM message is not computable ahead of execution. This generally means at least part
/// of the message is invalid, which could be due to it containing overly nested structures or an invalid
/// nested data segment (e.g. for the call in `Transact`).
WeightNotComputable,
/// The XCM did not pass the barrier condition for execution. The barrier condition differs on different
/// chains and in different circumstances, but generally it means that the conditions surrounding the message
/// were not such that the chain considers the message worth spending time executing. Since most chains
/// lift the barrier to execution on appropriate payment, presentation of an NFT voucher, or based on the
/// message origin, it means that none of those were the case.
Barrier,
/// Indicates that it is not possible for a location to have an asset be withdrawn or transferred from its
/// ownership. This probably means it doesn't own (enough of) it, but may also indicate that it is under a
/// lock, hold, freeze or is otherwise unavailable.
NotWithdrawable,
/// Indicates that the consensus system cannot deposit an asset under the ownership of a particular location.
LocationCannotHold,
/// The assets given to purchase weight is are insufficient for the weight desired.
TooExpensive,
/// The given asset is not handled.
AssetNotFound,
/// The given message cannot be translated into a format that the destination can be expected to interpret.
DestinationUnsupported,
}
impl From<()> for Error {
fn from(_: ()) -> Self {
Self::Undefined
}
}
pub type Result = result::Result<(), Error>;
/// Local weight type; execution time in picoseconds.
pub type Weight = u64;
/// Outcome of an XCM execution.
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug)]
pub enum Outcome {
/// Execution completed successfully; given weight was used.
Complete(Weight),
/// Execution started, but did not complete successfully due to the given error; given weight was used.
Incomplete(Weight, Error),
/// Execution did not start due to the given error.
Error(Error),
}
impl Outcome {
pub fn ensure_complete(self) -> Result {
match self {
Outcome::Complete(_) => Ok(()),
Outcome::Incomplete(_, e) => Err(e),
Outcome::Error(e) => Err(e),
}
}
pub fn ensure_execution(self) -> result::Result<Weight, Error> {
match self {
Outcome::Complete(w) => Ok(w),
Outcome::Incomplete(w, _) => Ok(w),
Outcome::Error(e) => Err(e),
}
}
/// How much weight was used by the XCM execution attempt.
pub fn weight_used(&self) -> Weight {
match self {
Outcome::Complete(w) => *w,
Outcome::Incomplete(w, _) => *w,
Outcome::Error(_) => 0,
}
}
}
/// Type of XCM message executor.
pub trait ExecuteXcm<Call> {
/// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. The weight limit is
/// a basic hard-limit and the implementation may place further restrictions or requirements on weight and
/// other aspects.
fn execute_xcm(origin: MultiLocation, message: Xcm<Call>, weight_limit: Weight) -> Outcome {
log::debug!(
target: "xcm::execute_xcm",
"origin: {:?}, message: {:?}, weight_limit: {:?}",
origin,
message,
weight_limit,
);
Self::execute_xcm_in_credit(origin, message, weight_limit, 0)
}
/// Execute some XCM `message` from `origin` using no more than `weight_limit` weight.
///
/// Some amount of `weight_credit` may be provided which, depending on the implementation, may allow
/// execution without associated payment.
fn execute_xcm_in_credit(
origin: MultiLocation,
message: Xcm<Call>,
weight_limit: Weight,
weight_credit: Weight,
) -> Outcome;
}
impl<C> ExecuteXcm<C> for () {
fn execute_xcm_in_credit(
_origin: MultiLocation,
_message: Xcm<C>,
_weight_limit: Weight,
_weight_credit: Weight,
) -> Outcome {
Outcome::Error(Error::Unimplemented)
}
}
/// Utility for sending an XCM message.
///
/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return
/// `CannotReachDestination` to pass the execution to the next sender item. Note that each `CannotReachDestination`
/// might alter the destination and the XCM message for to the next router.
///
///
/// # Example
/// ```rust
/// # use xcm::v1::{MultiLocation, Xcm, Junction, Error, OriginKind, SendXcm, Result};
/// # use parity_scale_codec::Encode;
///
/// /// A sender that only passes the message through and does nothing.
/// struct Sender1;
/// impl SendXcm for Sender1 {
/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
/// return Err(Error::CannotReachDestination(destination, message))
/// }
/// }
///
/// /// A sender that accepts a message that has an X2 junction, otherwise stops the routing.
/// struct Sender2;
/// impl SendXcm for Sender2 {
/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
/// if let MultiLocation::X2(j1, j2) = destination {
/// Ok(())
/// } else {
/// Err(Error::Undefined)
/// }
/// }
/// }
///
/// /// A sender that accepts a message from an X1 parent junction, passing through otherwise.
/// struct Sender3;
/// impl SendXcm for Sender3 {
/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
/// match destination {
/// MultiLocation::X1(j) if j == Junction::Parent => Ok(()),
/// _ => Err(Error::CannotReachDestination(destination, message)),
/// }
/// }
/// }
///
/// // A call to send via XCM. We don't really care about this.
/// # fn main() {
/// let call: Vec<u8> = ().encode();
/// let message = Xcm::Transact { origin_type: OriginKind::Superuser, require_weight_at_most: 0, call: call.into() };
/// let destination = MultiLocation::X1(Junction::Parent);
///
/// assert!(
/// // Sender2 will block this.
/// <(Sender1, Sender2, Sender3) as SendXcm>::send_xcm(destination.clone(), message.clone())
/// .is_err()
/// );
///
/// assert!(
/// // Sender3 will catch this.
/// <(Sender1, Sender3) as SendXcm>::send_xcm(destination.clone(), message.clone())
/// .is_ok()
/// );
/// # }
/// ```
pub trait SendXcm {
/// Send an XCM `message` to a given `destination`.
///
/// If it is not a destination which can be reached with this type but possibly could by others, then it *MUST*
/// return `CannotReachDestination`. Any other error will cause the tuple implementation to exit early without
/// trying other type fields.
fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl SendXcm for Tuple {
fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
for_tuples!( #(
// we shadow `destination` and `message` in each expansion for the next one.
let (destination, message) = match Tuple::send_xcm(destination, message) {
Err(Error::CannotReachDestination(d, m)) => (d, m),
o @ _ => return o,
};
)* );
Err(Error::CannotReachDestination(destination, message))
}
}
+3 -3
View File
@@ -19,7 +19,7 @@
use frame_support::{ensure, traits::Contains, weights::Weight};
use polkadot_parachain::primitives::IsSystem;
use sp_std::{marker::PhantomData, result::Result};
use xcm::v0::{Junction, MultiLocation, Order, Xcm};
use xcm::latest::{Junction, MultiLocation, Order, Xcm};
use xcm_executor::traits::{OnResponse, ShouldExecute};
/// Execution barrier that just takes `shallow_weight` from `weight_credit`.
@@ -51,9 +51,9 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFro
ensure!(T::contains(origin), ());
ensure!(top_level, ());
match message {
Xcm::TeleportAsset { effects, .. } |
Xcm::ReceiveTeleportedAsset { effects, .. } |
Xcm::WithdrawAsset { effects, .. } |
Xcm::ReserveAssetDeposit { effects, .. }
Xcm::ReserveAssetDeposited { effects, .. }
if matches!(
effects.first(),
Some(Order::BuyExecution { debt, ..}) if *debt >= shallow_weight
@@ -19,7 +19,7 @@
use frame_support::traits::{ExistenceRequirement::AllowDeath, Get, WithdrawReasons};
use sp_runtime::traits::{CheckedSub, SaturatedConversion};
use sp_std::{convert::TryInto, marker::PhantomData, result};
use xcm::v0::{Error as XcmError, MultiAsset, MultiLocation, Result};
use xcm::latest::{Error as XcmError, MultiAsset, MultiLocation, Result};
use xcm_executor::{
traits::{Convert, MatchesFungible, TransactAsset},
Assets,
@@ -53,7 +53,7 @@ impl From<Error> for XcmError {
/// # Example
/// ```
/// use frame_support::parameter_types;
/// use xcm::v0::{MultiLocation, Junction};
/// use xcm::latest::{MultiLocation, Junction};
/// use xcm_builder::{ParentIsDefault, CurrencyAdapter, IsConcrete};
///
/// /// Our chain's account id.
@@ -18,20 +18,20 @@
use frame_support::traits::Get;
use sp_std::marker::PhantomData;
use xcm::v0::{MultiAsset, MultiLocation};
use xcm::latest::{AssetId::Concrete, MultiAsset, MultiAssetFilter, MultiLocation};
use xcm_executor::traits::FilterAssetLocation;
/// Accepts an asset iff it is a native asset.
pub struct NativeAsset;
impl FilterAssetLocation for NativeAsset {
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
matches!(asset, MultiAsset::ConcreteFungible { ref id, .. } if id == origin)
matches!(asset.id, Concrete(ref id) if id == origin)
}
}
/// Accepts an asset if it is contained in the given `T`'s `Get` implementation.
pub struct Case<T>(PhantomData<T>);
impl<T: Get<(MultiAsset, MultiLocation)>> FilterAssetLocation for Case<T> {
impl<T: Get<(MultiAssetFilter, MultiLocation)>> FilterAssetLocation for Case<T> {
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
let (a, o) = T::get();
a.contains(asset) && &o == origin
@@ -18,7 +18,12 @@
use frame_support::traits::{tokens::fungibles, Contains, Get};
use sp_std::{borrow::Borrow, marker::PhantomData, prelude::*, result};
use xcm::v0::{Error as XcmError, Junction, MultiAsset, MultiLocation, Result};
use xcm::latest::{
AssetId::{Abstract, Concrete},
Error as XcmError,
Fungibility::Fungible,
Junction, MultiAsset, MultiLocation, Result,
};
use xcm_executor::traits::{Convert, Error as MatchError, MatchesFungibles, TransactAsset};
/// Converter struct implementing `AssetIdConversion` converting a numeric asset ID (must be `TryFrom/TryInto<u128>`) into
@@ -61,8 +66,8 @@ impl<
for ConvertedConcreteAssetId<AssetId, Balance, ConvertAssetId, ConvertBalance>
{
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> {
let (id, amount) = match a {
MultiAsset::ConcreteFungible { id, amount } => (id, amount),
let (amount, id) = match (&a.fun, &a.id) {
(Fungible(ref amount), Concrete(ref id)) => (amount, id),
_ => return Err(MatchError::AssetNotFound),
};
let what =
@@ -85,8 +90,8 @@ impl<
for ConvertedAbstractAssetId<AssetId, Balance, ConvertAssetId, ConvertBalance>
{
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> {
let (id, amount) = match a {
MultiAsset::AbstractFungible { id, amount } => (id, amount),
let (amount, id) = match (&a.fun, &a.id) {
(Fungible(ref amount), Abstract(ref id)) => (amount, id),
_ => return Err(MatchError::AssetNotFound),
};
let what =
+3 -1
View File
@@ -55,7 +55,9 @@ pub use fungibles_adapter::{
};
mod weight;
pub use weight::{FixedRateOfConcreteFungible, FixedWeightBounds, TakeRevenue, UsingComponents};
#[allow(deprecated)]
pub use weight::FixedRateOfConcreteFungible;
pub use weight::{FixedRateOfFungible, FixedWeightBounds, TakeRevenue, UsingComponents};
mod matches_fungible;
pub use matches_fungible::{IsAbstract, IsConcrete};
@@ -19,7 +19,7 @@ use parity_scale_codec::Encode;
use sp_io::hashing::blake2_256;
use sp_runtime::traits::AccountIdConversion;
use sp_std::{borrow::Borrow, marker::PhantomData};
use xcm::v0::{Junction, MultiLocation, NetworkId};
use xcm::latest::{Junction, MultiLocation, NetworkId};
use xcm_executor::traits::{Convert, InvertLocation};
pub struct Account32Hash<Network, AccountId>(PhantomData<(Network, AccountId)>);
@@ -155,7 +155,7 @@ impl<Network: Get<NetworkId>, AccountId: From<[u8; 20]> + Into<[u8; 20]> + Clone
/// ```
/// ```rust
/// # use frame_support::parameter_types;
/// # use xcm::v0::{MultiLocation::{self, *}, Junction::*, NetworkId::Any};
/// # use xcm::latest::{MultiLocation::{self, *}, Junction::*, NetworkId::Any};
/// # use xcm_builder::LocationInverter;
/// # use xcm_executor::traits::InvertLocation;
/// # fn main() {
@@ -200,7 +200,7 @@ mod tests {
use super::*;
use frame_support::parameter_types;
use xcm::v0::{Junction::*, MultiLocation::*, NetworkId::Any};
use xcm::latest::{Junction::*, MultiLocation::*, NetworkId::Any};
fn account20() -> Junction {
AccountKey20 { network: Any, key: Default::default() }
@@ -19,7 +19,11 @@
use frame_support::traits::Get;
use sp_runtime::traits::CheckedConversion;
use sp_std::{convert::TryFrom, marker::PhantomData};
use xcm::v0::{MultiAsset, MultiLocation};
use xcm::latest::{
AssetId::{Abstract, Concrete},
Fungibility::Fungible,
MultiAsset, MultiLocation,
};
use xcm_executor::traits::MatchesFungible;
/// Converts a `MultiAsset` into balance `B` if it is a concrete fungible with an id equal to that
@@ -28,17 +32,16 @@ use xcm_executor::traits::MatchesFungible;
/// # Example
///
/// ```
/// use xcm::v0::{MultiAsset, MultiLocation, Junction};
/// use xcm::latest::prelude::*;
/// use xcm_builder::IsConcrete;
/// use xcm_executor::traits::MatchesFungible;
///
/// frame_support::parameter_types! {
/// pub TargetLocation: MultiLocation = MultiLocation::X1(Junction::Parent);
/// pub TargetLocation: MultiLocation = X1(Parent);
/// }
///
/// # fn main() {
/// let id = MultiLocation::X1(Junction::Parent);
/// let asset = MultiAsset::ConcreteFungible { id, amount: 999u128 };
/// let asset = (X1(Parent), 999).into();
/// // match `asset` if it is a concrete asset in `TargetLocation`.
/// assert_eq!(<IsConcrete<TargetLocation> as MatchesFungible<u128>>::matches_fungible(&asset), Some(999));
/// # }
@@ -46,8 +49,8 @@ use xcm_executor::traits::MatchesFungible;
pub struct IsConcrete<T>(PhantomData<T>);
impl<T: Get<MultiLocation>, B: TryFrom<u128>> MatchesFungible<B> for IsConcrete<T> {
fn matches_fungible(a: &MultiAsset) -> Option<B> {
match a {
MultiAsset::ConcreteFungible { id, amount } if id == &T::get() =>
match (&a.id, &a.fun) {
(Concrete(ref id), Fungible(ref amount)) if id == &T::get() =>
CheckedConversion::checked_from(*amount),
_ => None,
}
@@ -59,7 +62,7 @@ impl<T: Get<MultiLocation>, B: TryFrom<u128>> MatchesFungible<B> for IsConcrete<
/// # Example
///
/// ```
/// use xcm::v0::{MultiAsset};
/// use xcm::latest::prelude::*;
/// use xcm_builder::IsAbstract;
/// use xcm_executor::traits::MatchesFungible;
///
@@ -68,7 +71,7 @@ impl<T: Get<MultiLocation>, B: TryFrom<u128>> MatchesFungible<B> for IsConcrete<
/// }
///
/// # fn main() {
/// let asset = MultiAsset::AbstractFungible { id: vec![7u8], amount: 999u128 };
/// let asset = (vec![7u8], 999).into();
/// // match `asset` if it is a concrete asset in `TargetLocation`.
/// assert_eq!(<IsAbstract<TargetLocation> as MatchesFungible<u128>>::matches_fungible(&asset), Some(999));
/// # }
@@ -76,8 +79,8 @@ impl<T: Get<MultiLocation>, B: TryFrom<u128>> MatchesFungible<B> for IsConcrete<
pub struct IsAbstract<T>(PhantomData<T>);
impl<T: Get<&'static [u8]>, B: TryFrom<u128>> MatchesFungible<B> for IsAbstract<T> {
fn matches_fungible(a: &MultiAsset) -> Option<B> {
match a {
MultiAsset::AbstractFungible { id, amount } if &id[..] == T::get() =>
match (&a.id, &a.fun) {
(Abstract(ref id), Fungible(ref amount)) if id == &T::get() =>
CheckedConversion::checked_from(*amount),
_ => None,
}
+18 -17
View File
@@ -16,7 +16,7 @@
pub use crate::{
AllowKnownQueryResponses, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom,
FixedRateOfConcreteFungible, FixedWeightBounds, LocationInverter, TakeWeightCredit,
FixedRateOfFungible, FixedWeightBounds, LocationInverter, TakeWeightCredit,
};
pub use frame_support::{
dispatch::{
@@ -34,10 +34,7 @@ pub use sp_std::{
fmt::Debug,
marker::PhantomData,
};
pub use xcm::v0::{
opaque, Error as XcmError, Junction, Junction::*, MultiAsset, MultiLocation, MultiLocation::*,
Order, OriginKind, Result as XcmResult, SendXcm, Xcm,
};
pub use xcm::latest::prelude::*;
pub use xcm_executor::{
traits::{ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset},
Assets, Config,
@@ -123,7 +120,7 @@ pub fn assets(who: u64) -> Vec<MultiAsset> {
ASSETS.with(|a| a.borrow().get(&who).map_or(vec![], |a| a.clone().into()))
}
pub fn add_asset(who: u64, what: MultiAsset) {
ASSETS.with(|a| a.borrow_mut().entry(who).or_insert(Assets::new()).saturating_subsume(what));
ASSETS.with(|a| a.borrow_mut().entry(who).or_insert(Assets::new()).subsume(what));
}
pub struct TestAssetTransactor;
@@ -140,8 +137,8 @@ impl TransactAsset for TestAssetTransactor {
a.borrow_mut()
.get_mut(&who)
.ok_or(XcmError::NotWithdrawable)?
.try_take(what.clone())
.map_err(|()| XcmError::NotWithdrawable)
.try_take(what.clone().into())
.map_err(|_| XcmError::NotWithdrawable)
})
}
}
@@ -155,7 +152,7 @@ pub fn to_account(l: MultiLocation) -> Result<u64, MultiLocation> {
// Children at 1000+id
X1(Parachain(id)) => 1000 + id as u64,
// Self at 3000
Null => 3000,
Here => 3000,
// Parent at 3001
X1(Parent) => 3001,
l => return Err(l),
@@ -181,14 +178,14 @@ impl ConvertOrigin<TestOrigin> for TestOriginConverter {
}
thread_local! {
pub static IS_RESERVE: RefCell<BTreeMap<MultiLocation, Vec<MultiAsset>>> = RefCell::new(BTreeMap::new());
pub static IS_TELEPORTER: RefCell<BTreeMap<MultiLocation, Vec<MultiAsset>>> = RefCell::new(BTreeMap::new());
pub static IS_RESERVE: RefCell<BTreeMap<MultiLocation, Vec<MultiAssetFilter>>> = RefCell::new(BTreeMap::new());
pub static IS_TELEPORTER: RefCell<BTreeMap<MultiLocation, Vec<MultiAssetFilter>>> = RefCell::new(BTreeMap::new());
}
pub fn add_reserve(from: MultiLocation, asset: MultiAsset) {
pub fn add_reserve(from: MultiLocation, asset: MultiAssetFilter) {
IS_RESERVE.with(|r| r.borrow_mut().entry(from).or_default().push(asset));
}
#[allow(dead_code)]
pub fn add_teleporter(from: MultiLocation, asset: MultiAsset) {
pub fn add_teleporter(from: MultiLocation, asset: MultiAssetFilter) {
IS_TELEPORTER.with(|r| r.borrow_mut().entry(from).or_default().push(asset));
}
pub struct TestIsReserve;
@@ -206,7 +203,7 @@ impl FilterAssetLocation for TestIsTeleporter {
}
}
use xcm::v0::Response;
use xcm::latest::Response;
pub enum ResponseSlot {
Expecting(MultiLocation),
Received(Response),
@@ -222,7 +219,11 @@ impl OnResponse for TestResponseHandler {
_ => false,
})
}
fn on_response(_origin: MultiLocation, query_id: u64, response: xcm::v0::Response) -> Weight {
fn on_response(
_origin: MultiLocation,
query_id: u64,
response: xcm::latest::Response,
) -> Weight {
QUERIES.with(|q| {
q.borrow_mut().entry(query_id).and_modify(|v| {
if matches!(*v, ResponseSlot::Expecting(..)) {
@@ -254,7 +255,7 @@ parameter_types! {
pub static AllowUnpaidFrom: Vec<MultiLocation> = vec![];
pub static AllowPaidFrom: Vec<MultiLocation> = vec![];
// 1_000_000_000_000 => 1 unit of asset for 1 unit of Weight.
pub static WeightPrice: (MultiLocation, u128) = (Null, 1_000_000_000_000);
pub static WeightPrice: (AssetId, u128) = (Here.into(), 1_000_000_000_000);
}
pub type TestBarrier = (
@@ -275,6 +276,6 @@ impl Config for TestConfig {
type LocationInverter = LocationInverter<TestAncestry>;
type Barrier = TestBarrier;
type Weigher = FixedWeightBounds<UnitWeightCost, TestCall>;
type Trader = FixedRateOfConcreteFungible<WeightPrice, ()>;
type Trader = FixedRateOfFungible<WeightPrice, ()>;
type ResponseHandler = TestResponseHandler;
}
@@ -20,7 +20,7 @@ use frame_support::traits::{EnsureOrigin, Get, GetBacking, OriginTrait};
use frame_system::RawOrigin as SystemRawOrigin;
use polkadot_parachain::primitives::IsSystem;
use sp_std::{convert::TryInto, marker::PhantomData};
use xcm::v0::{BodyId, BodyPart, Junction, MultiLocation, NetworkId, OriginKind};
use xcm::latest::{BodyId, BodyPart, Junction, MultiLocation, NetworkId, OriginKind};
use xcm_executor::traits::{Convert, ConvertOrigin};
/// Sovereign accounts use the system's `Signed` origin with an account ID derived from the `LocationConverter`.
@@ -170,7 +170,7 @@ where
// We institute a root fallback so root can always represent the context. This
// guarantees that `successful_origin` will work.
if o.caller() == Origin::root().caller() {
Ok(MultiLocation::Null)
Ok(MultiLocation::Here)
} else {
Err(o)
}
+78 -61
View File
@@ -15,16 +15,14 @@
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use super::{mock::*, *};
use xcm::v0::{ExecuteXcm, NetworkId::Any, Order, Outcome, Response};
use xcm::latest::prelude::*;
use xcm_executor::{traits::*, Config, XcmExecutor};
use MultiAsset::*;
use Option::None;
#[test]
fn basic_setup_works() {
add_reserve(X1(Parent), AllConcreteFungible { id: X1(Parent) });
add_reserve(X1(Parent), Wild((X1(Parent), WildFungible).into()));
assert!(<TestConfig as Config>::IsReserve::filter_asset_location(
&ConcreteFungible { id: X1(Parent), amount: 100 },
&(X1(Parent), 100).into(),
&X1(Parent),
));
@@ -34,22 +32,23 @@ fn basic_setup_works() {
assert_eq!(to_account(X2(Parent, Parachain(50))), Ok(2050));
assert_eq!(to_account(X1(AccountIndex64 { index: 1, network: Any })), Ok(1));
assert_eq!(to_account(X1(AccountIndex64 { index: 42, network: Any })), Ok(42));
assert_eq!(to_account(Null), Ok(3000));
assert_eq!(to_account(Here), Ok(3000));
}
#[test]
fn weigher_should_work() {
let mut message = opaque::Xcm::ReserveAssetDeposit {
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
let mut message = opaque::Xcm::ReserveAssetDeposited {
assets: (X1(Parent), 100).into(),
effects: vec![
Order::BuyExecution {
fees: All,
fees: (X1(Parent), 1).into(),
weight: 0,
debt: 30,
halt_on_error: true,
xcm: vec![],
orders: vec![],
instructions: vec![],
},
Order::DepositAsset { assets: vec![All], dest: Null },
Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here },
],
}
.into();
@@ -58,10 +57,8 @@ fn weigher_should_work() {
#[test]
fn take_weight_credit_barrier_should_work() {
let mut message = opaque::Xcm::TransferAsset {
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
dest: Null,
};
let mut message =
opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), beneficiary: Here };
let mut weight_credit = 10;
let r =
@@ -77,10 +74,8 @@ fn take_weight_credit_barrier_should_work() {
#[test]
fn allow_unpaid_should_work() {
let mut message = opaque::Xcm::TransferAsset {
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
dest: Null,
};
let mut message =
opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), beneficiary: Here };
AllowUnpaidFrom::set(vec![X1(Parent)]);
@@ -107,10 +102,8 @@ fn allow_unpaid_should_work() {
fn allow_paid_should_work() {
AllowPaidFrom::set(vec![X1(Parent)]);
let mut message = opaque::Xcm::TransferAsset {
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
dest: Null,
};
let mut message =
opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), beneficiary: Here };
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
&X1(Parachain(1)),
@@ -121,17 +114,19 @@ fn allow_paid_should_work() {
);
assert_eq!(r, Err(()));
let mut underpaying_message = opaque::Xcm::ReserveAssetDeposit {
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
let fees = (X1(Parent), 1).into();
let mut underpaying_message = opaque::Xcm::ReserveAssetDeposited {
assets: (X1(Parent), 100).into(),
effects: vec![
Order::BuyExecution {
fees: All,
fees,
weight: 0,
debt: 20,
halt_on_error: true,
xcm: vec![],
orders: vec![],
instructions: vec![],
},
Order::DepositAsset { assets: vec![All], dest: Null },
Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here },
],
};
@@ -144,17 +139,19 @@ fn allow_paid_should_work() {
);
assert_eq!(r, Err(()));
let mut paying_message = opaque::Xcm::ReserveAssetDeposit {
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
let fees = (X1(Parent), 1).into();
let mut paying_message = opaque::Xcm::ReserveAssetDeposited {
assets: (X1(Parent), 100).into(),
effects: vec![
Order::BuyExecution {
fees: All,
fees,
weight: 0,
debt: 30,
halt_on_error: true,
xcm: vec![],
orders: vec![],
instructions: vec![],
},
Order::DepositAsset { assets: vec![All], dest: Null },
Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here },
],
};
@@ -180,27 +177,33 @@ fn allow_paid_should_work() {
#[test]
fn paying_reserve_deposit_should_work() {
AllowPaidFrom::set(vec![X1(Parent)]);
add_reserve(X1(Parent), AllConcreteFungible { id: X1(Parent) });
WeightPrice::set((X1(Parent), 1_000_000_000_000));
add_reserve(X1(Parent), (Parent, WildFungible).into());
WeightPrice::set((Parent.into(), 1_000_000_000_000));
let origin = X1(Parent);
let message = Xcm::<TestCall>::ReserveAssetDeposit {
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
let fees = (X1(Parent), 30).into();
let message = Xcm::<TestCall>::ReserveAssetDeposited {
assets: (X1(Parent), 100).into(),
effects: vec![
Order::<TestCall>::BuyExecution {
fees: All,
fees,
weight: 0,
debt: 30,
halt_on_error: true,
xcm: vec![],
orders: vec![],
instructions: vec![],
},
Order::<TestCall>::DepositAsset {
assets: All.into(),
max_assets: 1,
beneficiary: Here,
},
Order::<TestCall>::DepositAsset { assets: vec![All], dest: Null },
],
};
let weight_limit = 50;
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
assert_eq!(r, Outcome::Complete(30));
assert_eq!(assets(3000), vec![ConcreteFungible { id: X1(Parent), amount: 70 }]);
assert_eq!(assets(3000), vec![(X1(Parent), 70).into()]);
}
#[test]
@@ -208,19 +211,19 @@ fn transfer_should_work() {
// we'll let them have message execution for free.
AllowUnpaidFrom::set(vec![X1(Parachain(1))]);
// Child parachain #1 owns 1000 tokens held by us in reserve.
add_asset(1001, ConcreteFungible { id: Null, amount: 1000 });
add_asset(1001, (Here, 1000).into());
// They want to transfer 100 of them to their sibling parachain #2
let r = XcmExecutor::<TestConfig>::execute_xcm(
X1(Parachain(1)),
Xcm::TransferAsset {
assets: vec![ConcreteFungible { id: Null, amount: 100 }],
dest: X1(AccountIndex64 { index: 3, network: Any }),
assets: (Here, 100).into(),
beneficiary: X1(AccountIndex64 { index: 3, network: Any }),
},
50,
);
assert_eq!(r, Outcome::Complete(10));
assert_eq!(assets(3), vec![ConcreteFungible { id: Null, amount: 100 }]);
assert_eq!(assets(1001), vec![ConcreteFungible { id: Null, amount: 900 }]);
assert_eq!(assets(3), vec![(Here, 100).into()]);
assert_eq!(assets(1001), vec![(Here, 900).into()]);
assert_eq!(sent_xcm(), vec![]);
}
@@ -228,7 +231,7 @@ fn transfer_should_work() {
fn reserve_transfer_should_work() {
AllowUnpaidFrom::set(vec![X1(Parachain(1))]);
// Child parachain #1 owns 1000 tokens held by us in reserve.
add_asset(1001, ConcreteFungible { id: Null, amount: 1000 });
add_asset(1001, (Here, 1000).into());
// The remote account owned by gav.
let three = X1(AccountIndex64 { index: 3, network: Any });
@@ -237,22 +240,30 @@ fn reserve_transfer_should_work() {
let r = XcmExecutor::<TestConfig>::execute_xcm(
X1(Parachain(1)),
Xcm::TransferReserveAsset {
assets: vec![ConcreteFungible { id: Null, amount: 100 }],
assets: (Here, 100).into(),
dest: X1(Parachain(2)),
effects: vec![Order::DepositAsset { assets: vec![All], dest: three.clone() }],
effects: vec![Order::DepositAsset {
assets: All.into(),
max_assets: 1,
beneficiary: three.clone(),
}],
},
50,
);
assert_eq!(r, Outcome::Complete(10));
assert_eq!(assets(1002), vec![ConcreteFungible { id: Null, amount: 100 }]);
assert_eq!(assets(1002), vec![(Here, 100).into()]);
assert_eq!(
sent_xcm(),
vec![(
X1(Parachain(2)),
Xcm::ReserveAssetDeposit {
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
effects: vec![Order::DepositAsset { assets: vec![All], dest: three }],
Xcm::ReserveAssetDeposited {
assets: (X1(Parent), 100).into(),
effects: vec![Order::DepositAsset {
assets: All.into(),
max_assets: 1,
beneficiary: three
}],
}
)]
);
@@ -307,32 +318,38 @@ fn transacting_should_refund_weight() {
fn paid_transacting_should_refund_payment_for_unused_weight() {
let one = X1(AccountIndex64 { index: 1, network: Any });
AllowPaidFrom::set(vec![one.clone()]);
add_asset(1, ConcreteFungible { id: X1(Parent), amount: 100 });
WeightPrice::set((X1(Parent), 1_000_000_000_000));
add_asset(1, (Parent, 100).into());
WeightPrice::set((Parent.into(), 1_000_000_000_000));
let origin = one.clone();
let fees = (X1(Parent), 100).into();
let message = Xcm::<TestCall>::WithdrawAsset {
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], // enough for 100 units of weight.
assets: (X1(Parent), 100).into(), // enough for 100 units of weight.
effects: vec![
Order::<TestCall>::BuyExecution {
fees: All,
fees,
weight: 70,
debt: 30,
halt_on_error: true,
xcm: vec![Xcm::<TestCall>::Transact {
orders: vec![],
instructions: vec![Xcm::<TestCall>::Transact {
origin_type: OriginKind::Native,
require_weight_at_most: 60,
// call estimated at 70 but only takes 10.
call: TestCall::Any(60, Some(10)).encode().into(),
}],
},
Order::<TestCall>::DepositAsset { assets: vec![All], dest: one.clone() },
Order::<TestCall>::DepositAsset {
assets: All.into(),
max_assets: 1,
beneficiary: one.clone(),
},
],
};
let weight_limit = 100;
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
assert_eq!(r, Outcome::Complete(50));
assert_eq!(assets(1), vec![ConcreteFungible { id: X1(Parent), amount: 50 }]);
assert_eq!(assets(1), vec![(X1(Parent), 50).into()]);
}
#[test]
@@ -342,7 +359,7 @@ fn prepaid_result_of_query_should_get_free_execution() {
// We put this in manually here, but normally this would be done at the point of crafting the message.
expect_response(query_id, origin.clone());
let the_response = Response::Assets(vec![ConcreteFungible { id: X1(Parent), amount: 100 }]);
let the_response = Response::Assets((X1(Parent), 100).into());
let message = Xcm::<TestCall>::QueryResponse { query_id, response: the_response.clone() };
let weight_limit = 10;
+119 -47
View File
@@ -21,7 +21,7 @@ use frame_support::{
use parity_scale_codec::Decode;
use sp_runtime::traits::{SaturatedConversion, Saturating, Zero};
use sp_std::{convert::TryInto, marker::PhantomData, result::Result};
use xcm::v0::{Error, MultiAsset, MultiLocation, Order, Xcm};
use xcm::latest::{AssetId, AssetId::Concrete, Error, MultiAsset, MultiLocation, Order, Xcm};
use xcm_executor::{
traits::{WeightBounds, WeightTrader},
Assets,
@@ -36,23 +36,13 @@ impl<T: Get<Weight>, C: Decode + GetDispatchInfo> WeightBounds<C> for FixedWeigh
Xcm::RelayedFrom { ref mut message, .. } =>
T::get().saturating_add(Self::shallow(message.as_mut())?),
Xcm::WithdrawAsset { effects, .. } |
Xcm::ReserveAssetDeposit { effects, .. } |
Xcm::TeleportAsset { effects, .. } => {
let inner: Weight = effects
.iter_mut()
.map(|effect| match effect {
Order::BuyExecution { .. } => {
// On success, execution of this will result in more weight being consumed but
// we don't count it here since this is only the *shallow*, non-negotiable weight
// spend and doesn't count weight placed behind a `BuyExecution` since it will not
// be definitely consumed from any existing weight credit if execution of the message
// is attempted.
T::get()
},
_ => T::get(),
})
.sum();
T::get().saturating_add(inner)
Xcm::ReserveAssetDeposited { effects, .. } |
Xcm::ReceiveTeleportedAsset { effects, .. } => {
let mut extra = T::get();
for order in effects.iter_mut() {
extra.saturating_accrue(Self::shallow_order(order)?);
}
extra
},
_ => T::get(),
})
@@ -61,19 +51,46 @@ impl<T: Get<Weight>, C: Decode + GetDispatchInfo> WeightBounds<C> for FixedWeigh
Ok(match message {
Xcm::RelayedFrom { ref mut message, .. } => Self::deep(message.as_mut())?,
Xcm::WithdrawAsset { effects, .. } |
Xcm::ReserveAssetDeposit { effects, .. } |
Xcm::TeleportAsset { effects, .. } => {
Xcm::ReserveAssetDeposited { effects, .. } |
Xcm::ReceiveTeleportedAsset { effects, .. } => {
let mut extra = 0;
for effect in effects.iter_mut() {
match effect {
Order::BuyExecution { xcm, .. } =>
for message in xcm.iter_mut() {
extra.saturating_accrue(
Self::shallow(message)?.saturating_add(Self::deep(message)?),
);
},
_ => {},
}
for order in effects.iter_mut() {
extra.saturating_accrue(Self::deep_order(order)?);
}
extra
},
_ => 0,
})
}
}
impl<T: Get<Weight>, C: Decode + GetDispatchInfo> FixedWeightBounds<T, C> {
fn shallow_order(order: &mut Order<C>) -> Result<Weight, ()> {
Ok(match order {
Order::BuyExecution { .. } => {
// On success, execution of this will result in more weight being consumed but
// we don't count it here since this is only the *shallow*, non-negotiable weight
// spend and doesn't count weight placed behind a `BuyExecution` since it will not
// be definitely consumed from any existing weight credit if execution of the message
// is attempted.
T::get()
},
_ => T::get(),
})
}
fn deep_order(order: &mut Order<C>) -> Result<Weight, ()> {
Ok(match order {
Order::BuyExecution { orders, instructions, .. } => {
let mut extra = 0;
for instruction in instructions.iter_mut() {
extra.saturating_accrue(
Self::shallow(instruction)?.saturating_add(Self::deep(instruction)?),
);
}
for order in orders.iter_mut() {
extra.saturating_accrue(
Self::shallow_order(order)?.saturating_add(Self::deep_order(order)?),
);
}
extra
},
@@ -98,11 +115,13 @@ impl TakeRevenue for () {
///
/// The constant `Get` type parameter should be the concrete fungible ID and the amount of it required for
/// one second of weight.
#[deprecated = "Use `FixedRateOfFungible` instead"]
pub struct FixedRateOfConcreteFungible<T: Get<(MultiLocation, u128)>, R: TakeRevenue>(
Weight,
u128,
PhantomData<(T, R)>,
);
#[allow(deprecated)]
impl<T: Get<(MultiLocation, u128)>, R: TakeRevenue> WeightTrader
for FixedRateOfConcreteFungible<T, R>
{
@@ -114,28 +133,80 @@ impl<T: Get<(MultiLocation, u128)>, R: TakeRevenue> WeightTrader
let (id, units_per_second) = T::get();
use frame_support::weights::constants::WEIGHT_PER_SECOND;
let amount = units_per_second * (weight as u128) / (WEIGHT_PER_SECOND as u128);
let required = MultiAsset::ConcreteFungible { amount, id };
let (unused, _) = payment.less(required).map_err(|_| Error::TooExpensive)?;
let unused = payment.checked_sub((id, amount).into()).map_err(|_| Error::TooExpensive)?;
self.0 = self.0.saturating_add(weight);
self.1 = self.1.saturating_add(amount);
Ok(unused)
}
fn refund_weight(&mut self, weight: Weight) -> MultiAsset {
fn refund_weight(&mut self, weight: Weight) -> Option<MultiAsset> {
let (id, units_per_second) = T::get();
let weight = weight.min(self.0);
let amount = units_per_second * (weight as u128) / 1_000_000_000_000u128;
self.0 -= weight;
self.1 = self.1.saturating_sub(amount);
let result = MultiAsset::ConcreteFungible { amount, id };
result
if amount > 0 {
Some((Concrete(id), amount).into())
} else {
None
}
}
}
#[allow(deprecated)]
impl<T: Get<(MultiLocation, u128)>, R: TakeRevenue> Drop for FixedRateOfConcreteFungible<T, R> {
fn drop(&mut self) {
if self.1 > 0 {
R::take_revenue((Concrete(T::get().0), self.1).into());
}
}
}
impl<T: Get<(MultiLocation, u128)>, R: TakeRevenue> Drop for FixedRateOfConcreteFungible<T, R> {
/// Simple fee calculator that requires payment in a single fungible at a fixed rate.
///
/// The constant `Get` type parameter should be the fungible ID and the amount of it required for
/// one second of weight.
pub struct FixedRateOfFungible<T: Get<(AssetId, u128)>, R: TakeRevenue>(
Weight,
u128,
PhantomData<(T, R)>,
);
impl<T: Get<(AssetId, u128)>, R: TakeRevenue> WeightTrader for FixedRateOfFungible<T, R> {
fn new() -> Self {
Self(0, 0, PhantomData)
}
fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, Error> {
let (id, units_per_second) = T::get();
use frame_support::weights::constants::WEIGHT_PER_SECOND;
let amount = units_per_second * (weight as u128) / (WEIGHT_PER_SECOND as u128);
if amount == 0 {
return Ok(payment)
}
let unused = payment.checked_sub((id, amount).into()).map_err(|_| Error::TooExpensive)?;
self.0 = self.0.saturating_add(weight);
self.1 = self.1.saturating_add(amount);
Ok(unused)
}
fn refund_weight(&mut self, weight: Weight) -> Option<MultiAsset> {
let (id, units_per_second) = T::get();
let weight = weight.min(self.0);
let amount = units_per_second * (weight as u128) / 1_000_000_000_000u128;
self.0 -= weight;
self.1 = self.1.saturating_sub(amount);
if amount > 0 {
Some((id, amount).into())
} else {
None
}
}
}
impl<T: Get<(AssetId, u128)>, R: TakeRevenue> Drop for FixedRateOfFungible<T, R> {
fn drop(&mut self) {
let revenue = MultiAsset::ConcreteFungible { amount: self.1, id: T::get().0 };
R::take_revenue(revenue);
if self.1 > 0 {
R::take_revenue((T::get().0, self.1).into());
}
}
}
@@ -166,24 +237,25 @@ impl<
fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, Error> {
let amount = WeightToFee::calc(&weight);
let required = MultiAsset::ConcreteFungible {
amount: amount.try_into().map_err(|_| Error::Overflow)?,
id: AssetId::get(),
};
let (unused, _) = payment.less(required).map_err(|_| Error::TooExpensive)?;
let u128_amount: u128 = amount.try_into().map_err(|_| Error::Overflow)?;
let required = (Concrete(AssetId::get()), u128_amount).into();
let unused = payment.checked_sub(required).map_err(|_| Error::TooExpensive)?;
self.0 = self.0.saturating_add(weight);
self.1 = self.1.saturating_add(amount);
Ok(unused)
}
fn refund_weight(&mut self, weight: Weight) -> MultiAsset {
fn refund_weight(&mut self, weight: Weight) -> Option<MultiAsset> {
let weight = weight.min(self.0);
let amount = WeightToFee::calc(&weight);
self.0 -= weight;
self.1 = self.1.saturating_sub(amount);
let result =
MultiAsset::ConcreteFungible { amount: amount.saturated_into(), id: AssetId::get() };
result
let amount: u128 = amount.saturated_into();
if amount > 0 {
Some((AssetId::get(), amount).into())
} else {
None
}
}
}
impl<
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -22,7 +22,7 @@ use frame_support::{
dispatch::{Dispatchable, Parameter},
weights::{GetDispatchInfo, PostDispatchInfo},
};
use xcm::v0::SendXcm;
use xcm::latest::SendXcm;
/// The trait to parameterize the `XcmExecutor`.
pub trait Config {
+51 -46
View File
@@ -22,8 +22,8 @@ use frame_support::{
weights::GetDispatchInfo,
};
use sp_std::{marker::PhantomData, prelude::*};
use xcm::v0::{
Error as XcmError, ExecuteXcm, MultiAsset, MultiLocation, Order, Outcome, Response, SendXcm,
use xcm::latest::{
Error as XcmError, ExecuteXcm, MultiAssets, MultiLocation, Order, Outcome, Response, SendXcm,
Xcm,
};
@@ -34,7 +34,7 @@ use traits::{
};
mod assets;
pub use assets::{AssetId, Assets};
pub use assets::Assets;
mod config;
pub use config::Config;
@@ -94,10 +94,10 @@ impl<Config: config::Config> ExecuteXcm<Config::Call> for XcmExecutor<Config> {
}
impl<Config: config::Config> XcmExecutor<Config> {
fn reanchored(mut assets: Assets, dest: &MultiLocation) -> Vec<MultiAsset> {
fn reanchored(mut assets: Assets, dest: &MultiLocation) -> MultiAssets {
let inv_dest = Config::LocationInverter::invert_location(&dest);
assets.prepend_location(&inv_dest);
assets.into_assets_iter().collect::<Vec<_>>()
assets.into_assets_iter().collect::<Vec<_>>().into()
}
/// Execute the XCM and return the portion of weight of `shallow_weight + deep_weight` that `message` did not use.
@@ -144,17 +144,15 @@ impl<Config: config::Config> XcmExecutor<Config> {
(origin, Xcm::WithdrawAsset { assets, effects }) => {
// Take `assets` from the origin account (on-chain) and place in holding.
let mut holding = Assets::default();
for asset in assets {
ensure!(!asset.is_wildcard(), XcmError::Wildcard);
for asset in assets.inner() {
let withdrawn = Config::AssetTransactor::withdraw_asset(&asset, &origin)?;
holding.saturating_subsume_all(withdrawn);
holding.subsume_assets(withdrawn);
}
Some((holding, effects))
},
(origin, Xcm::ReserveAssetDeposit { assets, effects }) => {
(origin, Xcm::ReserveAssetDeposited { assets, effects }) => {
// check whether we trust origin to be our reserve location for this asset.
for asset in assets.iter() {
ensure!(!asset.is_wildcard(), XcmError::Wildcard);
for asset in assets.inner() {
// We only trust the origin to send us assets that they identify as their
// sovereign assets.
ensure!(
@@ -162,31 +160,28 @@ impl<Config: config::Config> XcmExecutor<Config> {
XcmError::UntrustedReserveLocation
);
}
Some((Assets::from(assets), effects))
Some((assets.into(), effects))
},
(origin, Xcm::TransferAsset { assets, dest }) => {
(origin, Xcm::TransferAsset { assets, beneficiary }) => {
// Take `assets` from the origin account (on-chain) and place into dest account.
for asset in assets {
ensure!(!asset.is_wildcard(), XcmError::Wildcard);
Config::AssetTransactor::teleport_asset(&asset, &origin, &dest)?;
for asset in assets.inner() {
Config::AssetTransactor::beam_asset(&asset, &origin, &beneficiary)?;
}
None
},
(origin, Xcm::TransferReserveAsset { mut assets, dest, effects }) => {
// Take `assets` from the origin account (on-chain) and place into dest account.
let inv_dest = Config::LocationInverter::invert_location(&dest);
for asset in assets.iter_mut() {
ensure!(!asset.is_wildcard(), XcmError::Wildcard);
Config::AssetTransactor::teleport_asset(&asset, &origin, &dest)?;
asset.reanchor(&inv_dest)?;
for asset in assets.inner() {
Config::AssetTransactor::beam_asset(asset, &origin, &dest)?;
}
Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposit { assets, effects })?;
assets.reanchor(&inv_dest)?;
Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposited { assets, effects })?;
None
},
(origin, Xcm::TeleportAsset { assets, effects }) => {
(origin, Xcm::ReceiveTeleportedAsset { assets, effects }) => {
// check whether we trust origin to teleport this asset to us via config trait.
for asset in assets.iter() {
ensure!(!asset.is_wildcard(), XcmError::Wildcard);
for asset in assets.inner() {
// We only trust the origin to send us assets that they identify as their
// sovereign assets.
ensure!(
@@ -198,7 +193,7 @@ impl<Config: config::Config> XcmExecutor<Config> {
// don't want to punish a possibly innocent chain/user).
Config::AssetTransactor::can_check_in(&origin, asset)?;
}
for asset in assets.iter() {
for asset in assets.inner() {
Config::AssetTransactor::check_in(&origin, asset);
}
Some((Assets::from(assets), effects))
@@ -252,41 +247,41 @@ impl<Config: config::Config> XcmExecutor<Config> {
if let Some((mut holding, effects)) = maybe_holding_effects {
for effect in effects.into_iter() {
total_surplus += Self::execute_effects(&origin, &mut holding, effect, trader)?;
total_surplus += Self::execute_orders(&origin, &mut holding, effect, trader)?;
}
}
Ok(total_surplus)
}
fn execute_effects(
fn execute_orders(
origin: &MultiLocation,
holding: &mut Assets,
effect: Order<Config::Call>,
order: Order<Config::Call>,
trader: &mut Config::Trader,
) -> Result<Weight, XcmError> {
log::trace!(
target: "xcm::execute_effects",
target: "xcm::execute_orders",
"origin: {:?}, holding: {:?}, effect: {:?}",
origin,
holding,
effect,
order,
);
let mut total_surplus = 0;
match effect {
Order::DepositAsset { assets, dest } => {
let deposited = holding.saturating_take(assets);
match order {
Order::DepositAsset { assets, max_assets, beneficiary } => {
let deposited = holding.limited_saturating_take(assets, max_assets as usize);
for asset in deposited.into_assets_iter() {
Config::AssetTransactor::deposit_asset(&asset, &dest)?;
Config::AssetTransactor::deposit_asset(&asset, &beneficiary)?;
}
},
Order::DepositReserveAsset { assets, dest, effects } => {
let deposited = holding.saturating_take(assets);
Order::DepositReserveAsset { assets, max_assets, dest, effects } => {
let deposited = holding.limited_saturating_take(assets, max_assets as usize);
for asset in deposited.assets_iter() {
Config::AssetTransactor::deposit_asset(&asset, &dest)?;
}
let assets = Self::reanchored(deposited, &dest);
Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposit { assets, effects })?;
Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposited { assets, effects })?;
},
Order::InitiateReserveWithdraw { assets, reserve, effects } => {
let assets = Self::reanchored(holding.saturating_take(assets), &reserve);
@@ -299,29 +294,37 @@ impl<Config: config::Config> XcmExecutor<Config> {
Config::AssetTransactor::check_out(&origin, &asset);
}
let assets = Self::reanchored(assets, &dest);
Config::XcmSender::send_xcm(dest, Xcm::TeleportAsset { assets, effects })?;
Config::XcmSender::send_xcm(dest, Xcm::ReceiveTeleportedAsset { assets, effects })?;
},
Order::QueryHolding { query_id, dest, assets } => {
let assets = Self::reanchored(holding.min(assets.iter()), &dest);
let assets = Self::reanchored(holding.min(&assets), &dest);
Config::XcmSender::send_xcm(
dest,
Xcm::QueryResponse { query_id, response: Response::Assets(assets) },
)?;
},
Order::BuyExecution { fees, weight, debt, halt_on_error, xcm } => {
// pay for `weight` using up to `fees` of the holding account.
Order::BuyExecution { fees, weight, debt, halt_on_error, orders, instructions } => {
// pay for `weight` using up to `fees` of the holding register.
let purchasing_weight =
Weight::from(weight.checked_add(debt).ok_or(XcmError::Overflow)?);
let max_fee = holding.try_take(fees).map_err(|()| XcmError::NotHoldingFees)?;
let max_fee =
holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?;
let unspent = trader.buy_weight(purchasing_weight, max_fee)?;
holding.saturating_subsume_all(unspent);
holding.subsume_assets(unspent);
let mut remaining_weight = weight;
for message in xcm.into_iter() {
for order in orders.into_iter() {
match Self::execute_orders(origin, holding, order, trader) {
Err(e) if halt_on_error => return Err(e),
Err(_) => {},
Ok(surplus) => total_surplus += surplus,
}
}
for instruction in instructions.into_iter() {
match Self::do_execute_xcm(
origin.clone(),
false,
message,
instruction,
&mut remaining_weight,
None,
trader,
@@ -331,7 +334,9 @@ impl<Config: config::Config> XcmExecutor<Config> {
Ok(surplus) => total_surplus += surplus,
}
}
holding.saturating_subsume(trader.refund_weight(remaining_weight));
if let Some(w) = trader.refund_weight(remaining_weight) {
holding.subsume(w);
}
},
_ => return Err(XcmError::UnhandledEffect)?,
}
@@ -16,7 +16,7 @@
use parity_scale_codec::{Decode, Encode};
use sp_std::{borrow::Borrow, convert::TryFrom, prelude::*, result::Result};
use xcm::v0::{MultiLocation, OriginKind};
use xcm::latest::{MultiLocation, OriginKind};
/// Generic third-party conversion trait. Use this when you don't want to force the user to use default
/// implementations of `From` and `Into` for the types you wish to convert between.
@@ -139,7 +139,7 @@ impl<T: Clone + Encode + Decode> Convert<Vec<u8>, T> for Decoded {
/// which is passed to the next convert item.
///
/// ```rust
/// # use xcm::v0::{MultiLocation, Junction, OriginKind};
/// # use xcm::latest::{MultiLocation, Junction, OriginKind};
/// # use xcm_executor::traits::ConvertOrigin;
/// // A convertor that will bump the para id and pass it to the next one.
/// struct BumpParaId;
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use xcm::v0::{MultiAsset, MultiLocation};
use xcm::latest::{MultiAsset, MultiLocation};
/// Filters assets/location pairs.
///
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use xcm::v0::MultiAsset;
use xcm::latest::MultiAsset;
pub trait MatchesFungible<Balance> {
fn matches_fungible(a: &MultiAsset) -> Option<Balance>;
@@ -15,7 +15,7 @@
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use sp_std::result;
use xcm::v0::{Error as XcmError, MultiAsset};
use xcm::latest::{Error as XcmError, MultiAsset};
/// Errors associated with [`MatchesFungibles`] operation.
pub enum Error {
@@ -15,7 +15,7 @@
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use frame_support::weights::Weight;
use xcm::v0::{MultiLocation, Response};
use xcm::latest::{MultiLocation, Response};
/// Define what needs to be done upon receiving a query response.
pub trait OnResponse {
@@ -16,7 +16,7 @@
use frame_support::weights::Weight;
use sp_std::result::Result;
use xcm::v0::{MultiLocation, Xcm};
use xcm::latest::{MultiLocation, Xcm};
/// Trait to determine whether the execution engine should actually execute a given XCM.
///
@@ -16,7 +16,7 @@
use crate::Assets;
use sp_std::result::Result;
use xcm::v0::{Error as XcmError, MultiAsset, MultiLocation, Result as XcmResult};
use xcm::latest::{Error as XcmError, MultiAsset, MultiLocation, Result as XcmResult};
/// Facility for asset transacting.
///
@@ -89,7 +89,7 @@ pub trait TransactAsset {
/// Move an `asset` `from` one location in `to` another location.
///
/// Attempts to use `transfer_asset` and if not available then falls back to using a two-part withdraw/deposit.
fn teleport_asset(
fn beam_asset(
asset: &MultiAsset,
from: &MultiLocation,
to: &MultiLocation,
@@ -111,7 +111,7 @@ impl TransactAsset for Tuple {
fn can_check_in(origin: &MultiLocation, what: &MultiAsset) -> XcmResult {
for_tuples!( #(
match Tuple::can_check_in(origin, what) {
Err(XcmError::AssetNotFound | XcmError::Unimplemented) => (),
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
@@ -139,7 +139,7 @@ impl TransactAsset for Tuple {
fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> XcmResult {
for_tuples!( #(
match Tuple::deposit_asset(what, who) {
Err(XcmError::AssetNotFound | XcmError::Unimplemented) => (),
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
@@ -155,7 +155,7 @@ impl TransactAsset for Tuple {
fn withdraw_asset(what: &MultiAsset, who: &MultiLocation) -> Result<Assets, XcmError> {
for_tuples!( #(
match Tuple::withdraw_asset(what, who) {
Err(XcmError::AssetNotFound | XcmError::Unimplemented) => (),
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
@@ -175,7 +175,7 @@ impl TransactAsset for Tuple {
) -> Result<Assets, XcmError> {
for_tuples!( #(
match Tuple::transfer_asset(what, from, to) {
Err(XcmError::AssetNotFound | XcmError::Unimplemented) => (),
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
r => return r,
}
)* );
@@ -193,6 +193,7 @@ impl TransactAsset for Tuple {
#[cfg(test)]
mod tests {
use super::*;
use MultiLocation::Here;
pub struct UnimplementedTransactor;
impl TransactAsset for UnimplementedTransactor {}
@@ -272,7 +273,7 @@ mod tests {
(UnimplementedTransactor, NotFoundTransactor, UnimplementedTransactor);
assert_eq!(
MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null),
MultiTransactor::deposit_asset(&(Here, 1).into(), &Here),
Err(XcmError::AssetNotFound)
);
}
@@ -281,7 +282,7 @@ mod tests {
fn unimplemented_and_not_found_continue_iteration() {
type MultiTransactor = (UnimplementedTransactor, NotFoundTransactor, SuccessfulTransactor);
assert_eq!(MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null), Ok(()));
assert_eq!(MultiTransactor::deposit_asset(&(Here, 1).into(), &Here), Ok(()));
}
#[test]
@@ -289,7 +290,7 @@ mod tests {
type MultiTransactor = (OverflowTransactor, SuccessfulTransactor);
assert_eq!(
MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null),
MultiTransactor::deposit_asset(&(Here, 1).into(), &Here),
Err(XcmError::Overflow)
);
}
@@ -298,6 +299,6 @@ mod tests {
fn success_stops_iteration() {
type MultiTransactor = (SuccessfulTransactor, OverflowTransactor);
assert_eq!(MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null), Ok(()));
assert_eq!(MultiTransactor::deposit_asset(&(Here, 1).into(), &Here), Ok(()));
}
}
@@ -17,7 +17,7 @@
use crate::Assets;
use frame_support::weights::Weight;
use sp_std::result::Result;
use xcm::v0::{Error, MultiAsset, MultiLocation, Xcm};
use xcm::latest::{Error, MultiAsset, MultiLocation, Xcm};
/// Determine the weight of an XCM message.
pub trait WeightBounds<Call> {
@@ -71,8 +71,8 @@ pub trait WeightTrader: Sized {
/// purchased using `buy_weight`.
///
/// Default implementation refunds nothing.
fn refund_weight(&mut self, _weight: Weight) -> MultiAsset {
MultiAsset::None
fn refund_weight(&mut self, _weight: Weight) -> Option<MultiAsset> {
None
}
}
+9 -15
View File
@@ -17,10 +17,9 @@
mod parachain;
mod relay_chain;
use sp_runtime::AccountId32;
use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain};
pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]);
pub const ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([0u8; 32]);
decl_test_parachain! {
pub struct ParaA {
@@ -100,13 +99,7 @@ mod tests {
use codec::Encode;
use frame_support::assert_ok;
use xcm::v0::{
Junction::{self, Parachain, Parent},
MultiAsset::*,
MultiLocation::*,
NetworkId, OriginKind,
Xcm::*,
};
use xcm::latest::prelude::*;
use xcm_simulator::TestExt;
#[test]
@@ -118,7 +111,7 @@ mod tests {
);
Relay::execute_with(|| {
assert_ok!(RelayChainPalletXcm::send_xcm(
Null,
Here,
X1(Parachain(1)),
Transact {
origin_type: OriginKind::SovereignAccount,
@@ -145,7 +138,7 @@ mod tests {
);
ParaA::execute_with(|| {
assert_ok!(ParachainPalletXcm::send_xcm(
Null,
Here,
X1(Parent),
Transact {
origin_type: OriginKind::SovereignAccount,
@@ -172,7 +165,7 @@ mod tests {
);
ParaA::execute_with(|| {
assert_ok!(ParachainPalletXcm::send_xcm(
Null,
Here,
X2(Parent, Parachain(2)),
Transact {
origin_type: OriginKind::SovereignAccount,
@@ -198,9 +191,10 @@ mod tests {
assert_ok!(RelayChainPalletXcm::reserve_transfer_assets(
relay_chain::Origin::signed(ALICE),
X1(Parachain(1)),
X1(Junction::AccountId32 { network: NetworkId::Any, id: ALICE.into() }),
vec![ConcreteFungible { id: Null, amount: 123 }],
123,
X1(AccountId32 { network: Any, id: ALICE.into() }),
(Here, 123).into(),
0,
3,
));
});
@@ -35,19 +35,10 @@ use polkadot_core_primitives::BlockNumber as RelayBlockNumber;
use polkadot_parachain::primitives::{
DmpMessageHandler, Id as ParaId, Sibling, XcmpMessageFormat, XcmpMessageHandler,
};
use xcm::{
v0::{
Error as XcmError, ExecuteXcm,
Junction::{Parachain, Parent},
MultiAsset,
MultiLocation::{self, X1},
NetworkId, Outcome, Xcm,
},
VersionedXcm,
};
use xcm::{latest::prelude::*, VersionedXcm};
use xcm_builder::{
AccountId32Aliases, AllowUnpaidExecutionFrom, CurrencyAdapter as XcmCurrencyAdapter,
EnsureXcmOrigin, FixedRateOfConcreteFungible, FixedWeightBounds, IsConcrete, LocationInverter,
EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, IsConcrete, LocationInverter,
NativeAsset, ParentIsDefault, SiblingParachainConvertsVia, SignedAccountId32AsNative,
SignedToAccountId32, SovereignSignedViaLocation,
};
@@ -129,7 +120,7 @@ pub type XcmOriginToCallOrigin = (
parameter_types! {
pub const UnitWeightCost: Weight = 1;
pub KsmPerSecond: (MultiLocation, u128) = (X1(Parent), 1);
pub KsmPerSecond: (AssetId, u128) = (Concrete(X1(Parent)), 1);
}
pub type LocalAssetTransactor =
@@ -149,7 +140,7 @@ impl Config for XcmConfig {
type LocationInverter = LocationInverter<Ancestry>;
type Barrier = Barrier;
type Weigher = FixedWeightBounds<UnitWeightCost, Call>;
type Trader = FixedRateOfConcreteFungible<KsmPerSecond, ()>;
type Trader = FixedRateOfFungible<KsmPerSecond, ()>;
type ResponseHandler = ();
}
@@ -26,13 +26,12 @@ use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32};
use polkadot_parachain::primitives::Id as ParaId;
use polkadot_runtime_parachains::{configuration, origin, shared, ump};
use xcm::v0::{MultiAsset, MultiLocation, NetworkId};
use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative,
ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfConcreteFungible, FixedWeightBounds,
IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation,
CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete,
LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation,
};
use xcm_executor::{Config, XcmExecutor};
@@ -92,10 +91,10 @@ impl shared::Config for Runtime {}
impl configuration::Config for Runtime {}
parameter_types! {
pub const KsmLocation: MultiLocation = MultiLocation::Null;
pub const KsmLocation: MultiLocation = MultiLocation::Here;
pub const KusamaNetwork: NetworkId = NetworkId::Kusama;
pub const AnyNetwork: NetworkId = NetworkId::Any;
pub Ancestry: MultiLocation = MultiLocation::Null;
pub Ancestry: MultiLocation = MultiLocation::Here;
pub UnitWeightCost: Weight = 1_000;
}
@@ -114,7 +113,7 @@ type LocalOriginConverter = (
parameter_types! {
pub const BaseXcmWeight: Weight = 1_000;
pub KsmPerSecond: (MultiLocation, u128) = (KsmLocation::get(), 1);
pub KsmPerSecond: (AssetId, u128) = (Concrete(KsmLocation::get()), 1);
}
pub type XcmRouter = super::RelayChainXcmRouter;
@@ -131,7 +130,7 @@ impl Config for XcmConfig {
type LocationInverter = LocationInverter<Ancestry>;
type Barrier = Barrier;
type Weigher = FixedWeightBounds<BaseXcmWeight, Call>;
type Trader = FixedRateOfConcreteFungible<KsmPerSecond, ()>;
type Trader = FixedRateOfFungible<KsmPerSecond, ()>;
type ResponseHandler = ();
}
+1 -1
View File
@@ -32,7 +32,7 @@ pub use polkadot_runtime_parachains::{
dmp,
ump::{self, MessageId, UmpSink, XcmSink},
};
pub use xcm::{v0::prelude::*, VersionedXcm};
pub use xcm::{latest::prelude::*, VersionedXcm};
pub use xcm_executor::XcmExecutor;
pub trait TestExt {