mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-07 22:28:02 +00:00
XCM v1 (#2815)
* 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:
@@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>)>;
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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>)>;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>)>;
|
||||
|
||||
@@ -74,6 +74,7 @@ fedora/M
|
||||
finalize/B
|
||||
FRAME/MS
|
||||
FSMs
|
||||
fungibility
|
||||
gameable
|
||||
getter/MS
|
||||
GiB/S
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
@@ -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
@@ -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()?),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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).
|
||||
@@ -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()?),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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()?))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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 =
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = ();
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user