mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-16 21:21:03 +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 parity_scale_codec::Encode;
|
||||||
use runtime_parachains::{configuration, dmp};
|
use runtime_parachains::{configuration, dmp};
|
||||||
use sp_std::marker::PhantomData;
|
use sp_std::marker::PhantomData;
|
||||||
use xcm::opaque::{
|
use xcm::opaque::v1::{Error, Junction, MultiLocation, Result, SendXcm, Xcm};
|
||||||
v0::{Error, Junction, MultiLocation, Result, SendXcm, Xcm},
|
|
||||||
VersionedXcm,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// XCM sender for relay chain. It only sends downward message.
|
/// 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 {
|
fn send_xcm(dest: MultiLocation, msg: Xcm) -> Result {
|
||||||
match dest {
|
match &dest {
|
||||||
MultiLocation::X1(Junction::Parachain(id)) => {
|
MultiLocation::X1(Junction::Parachain(id)) => {
|
||||||
// Downward message passing.
|
// Downward message passing.
|
||||||
|
let versioned_xcm =
|
||||||
|
W::wrap_version(&dest, msg).map_err(|()| Error::DestinationUnsupported)?;
|
||||||
let config = <configuration::Pallet<T>>::config();
|
let config = <configuration::Pallet<T>>::config();
|
||||||
<dmp::Pallet<T>>::queue_downward_message(
|
<dmp::Pallet<T>>::queue_downward_message(
|
||||||
&config,
|
&config,
|
||||||
id.into(),
|
(*id).into(),
|
||||||
VersionedXcm::from(msg).encode(),
|
versioned_xcm.encode(),
|
||||||
)
|
)
|
||||||
.map_err(Into::<Error>::into)?;
|
.map_err(Into::<Error>::into)?;
|
||||||
Ok(())
|
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::NativeVersion;
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
use static_assertions::const_assert;
|
use static_assertions::const_assert;
|
||||||
use xcm::v0::{
|
use xcm::latest::prelude::*;
|
||||||
BodyId,
|
|
||||||
Junction::Parachain,
|
|
||||||
MultiAsset::{self, AllConcreteFungible},
|
|
||||||
MultiLocation::{self, Null, X1},
|
|
||||||
NetworkId, Xcm,
|
|
||||||
};
|
|
||||||
use xcm_builder::{
|
use xcm_builder::{
|
||||||
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom,
|
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom,
|
||||||
BackingToPlurality, ChildParachainAsNative, ChildParachainConvertsVia,
|
BackingToPlurality, ChildParachainAsNative, ChildParachainConvertsVia,
|
||||||
@@ -1205,14 +1199,14 @@ impl auctions::Config for Runtime {
|
|||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
/// The location of the KSM token, from the context of this chain. Since this token is native to this
|
/// 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".
|
/// the context".
|
||||||
pub const KsmLocation: MultiLocation = MultiLocation::Null;
|
pub const KsmLocation: MultiLocation = MultiLocation::Here;
|
||||||
/// The Kusama network ID. This is named.
|
/// The Kusama network ID. This is named.
|
||||||
pub const KusamaNetwork: NetworkId = NetworkId::Kusama;
|
pub const KusamaNetwork: NetworkId = NetworkId::Kusama;
|
||||||
/// Our XCM location ancestry - i.e. what, if anything, `Parent` means evaluated in our context. Since
|
/// 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.
|
/// 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).
|
/// The check account, which holds any native assets that have been teleported out and not back in (yet).
|
||||||
pub CheckAccount: AccountId = XcmPallet::check_account();
|
pub CheckAccount: AccountId = XcmPallet::check_account();
|
||||||
}
|
}
|
||||||
@@ -1264,12 +1258,12 @@ parameter_types! {
|
|||||||
/// individual routers.
|
/// individual routers.
|
||||||
pub type XcmRouter = (
|
pub type XcmRouter = (
|
||||||
// Only one router so far - use DMP to communicate with child parachains.
|
// Only one router so far - use DMP to communicate with child parachains.
|
||||||
xcm_sender::ChildParachainRouter<Runtime>,
|
xcm_sender::ChildParachainRouter<Runtime, xcm::AlwaysRelease>,
|
||||||
);
|
);
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const KusamaForStatemint: (MultiAsset, MultiLocation) =
|
pub const Kusama: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(KsmLocation::get()) });
|
||||||
(AllConcreteFungible { id: Null }, X1(Parachain(1000)));
|
pub const KusamaForStatemint: (MultiAssetFilter, MultiLocation) = (Kusama::get(), X1(Parachain(1000)));
|
||||||
}
|
}
|
||||||
pub type TrustedTeleporters = (xcm_builder::Case<KusamaForStatemint>,);
|
pub type TrustedTeleporters = (xcm_builder::Case<KusamaForStatemint>,);
|
||||||
|
|
||||||
@@ -1317,65 +1311,14 @@ pub type LocalOriginToLocation = (
|
|||||||
SignedToAccountId32<Origin, AccountId, KusamaNetwork>,
|
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 {
|
impl pallet_xcm::Config for Runtime {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
||||||
type XcmRouter = XcmRouter;
|
type XcmRouter = XcmRouter;
|
||||||
// Anyone can execute XCM messages locally...
|
// Anyone can execute XCM messages locally...
|
||||||
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
||||||
// ...but they must match our filter, which requires them to be a simple withdraw + teleport.
|
// ...but they must match our filter, which rejects all.
|
||||||
type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts;
|
type XcmExecuteFilter = ();
|
||||||
type XcmExecutor = XcmExecutor<XcmConfig>;
|
type XcmExecutor = XcmExecutor<XcmConfig>;
|
||||||
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
|
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
|
||||||
type XcmReserveTransferFilter = 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 primitives::v1::{DownwardMessage, Hash, Id as ParaId, InboundDownwardMessage};
|
||||||
use sp_runtime::traits::{BlakeTwo256, Hash as HashT, SaturatedConversion};
|
use sp_runtime::traits::{BlakeTwo256, Hash as HashT, SaturatedConversion};
|
||||||
use sp_std::{fmt, prelude::*};
|
use sp_std::{fmt, prelude::*};
|
||||||
use xcm::v0::Error as XcmError;
|
use xcm::latest::Error as XcmError;
|
||||||
|
|
||||||
pub use pallet::*;
|
pub use pallet::*;
|
||||||
|
|
||||||
|
|||||||
@@ -1007,7 +1007,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
|
|
||||||
let notification_bytes = {
|
let notification_bytes = {
|
||||||
use parity_scale_codec::Encode as _;
|
use parity_scale_codec::Encode as _;
|
||||||
use xcm::opaque::{v0::Xcm, VersionedXcm};
|
use xcm::opaque::{v1::Xcm, VersionedXcm};
|
||||||
|
|
||||||
VersionedXcm::from(Xcm::HrmpNewChannelOpenRequest {
|
VersionedXcm::from(Xcm::HrmpNewChannelOpenRequest {
|
||||||
sender: u32::from(origin),
|
sender: u32::from(origin),
|
||||||
@@ -1066,7 +1066,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
|
|
||||||
let notification_bytes = {
|
let notification_bytes = {
|
||||||
use parity_scale_codec::Encode as _;
|
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()
|
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 config = <configuration::Pallet<T>>::config();
|
||||||
let notification_bytes = {
|
let notification_bytes = {
|
||||||
use parity_scale_codec::Encode as _;
|
use parity_scale_codec::Encode as _;
|
||||||
use xcm::opaque::{v0::Xcm, VersionedXcm};
|
use xcm::opaque::{v1::Xcm, VersionedXcm};
|
||||||
|
|
||||||
VersionedXcm::from(Xcm::HrmpChannelClosing {
|
VersionedXcm::from(Xcm::HrmpChannelClosing {
|
||||||
initiator: u32::from(origin),
|
initiator: u32::from(origin),
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ use sp_std::{
|
|||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use xcm::v0::Outcome;
|
use xcm::latest::Outcome;
|
||||||
|
|
||||||
pub use pallet::*;
|
pub use pallet::*;
|
||||||
|
|
||||||
@@ -78,14 +78,14 @@ pub type MessageId = [u8; 32];
|
|||||||
/// and will be forwarded to the XCM Executor.
|
/// and will be forwarded to the XCM Executor.
|
||||||
pub struct XcmSink<XcmExecutor, Config>(PhantomData<(XcmExecutor, Config)>);
|
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(
|
fn process_upward_message(
|
||||||
origin: ParaId,
|
origin: ParaId,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
max_weight: Weight,
|
max_weight: Weight,
|
||||||
) -> Result<Weight, (MessageId, Weight)> {
|
) -> Result<Weight, (MessageId, Weight)> {
|
||||||
use xcm::{
|
use xcm::{
|
||||||
v0::{Error as XcmError, Junction, MultiLocation, Xcm},
|
latest::{Error as XcmError, Junction, MultiLocation, Xcm},
|
||||||
VersionedXcm,
|
VersionedXcm,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ use polkadot_parachain::primitives::Id as ParaId;
|
|||||||
|
|
||||||
use constants::{currency::*, fee::*, time::*};
|
use constants::{currency::*, fee::*, time::*};
|
||||||
use frame_support::traits::InstanceFilter;
|
use frame_support::traits::InstanceFilter;
|
||||||
use xcm::v0::{BodyId, MultiLocation, NetworkId, Xcm};
|
use xcm::latest::prelude::*;
|
||||||
use xcm_builder::{
|
use xcm_builder::{
|
||||||
AccountId32Aliases, BackingToPlurality, ChildParachainAsNative, ChildParachainConvertsVia,
|
AccountId32Aliases, BackingToPlurality, ChildParachainAsNative, ChildParachainConvertsVia,
|
||||||
ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedWeightBounds,
|
ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedWeightBounds,
|
||||||
@@ -583,9 +583,9 @@ impl parachains_paras::Config for Runtime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const RocLocation: MultiLocation = MultiLocation::Null;
|
pub const RocLocation: MultiLocation = MultiLocation::Here;
|
||||||
pub const RococoNetwork: NetworkId = NetworkId::Polkadot;
|
pub const RococoNetwork: NetworkId = NetworkId::Polkadot;
|
||||||
pub const Ancestry: MultiLocation = MultiLocation::Null;
|
pub const Ancestry: MultiLocation = MultiLocation::Here;
|
||||||
pub CheckAccount: AccountId = XcmPallet::check_account();
|
pub CheckAccount: AccountId = XcmPallet::check_account();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -620,24 +620,15 @@ parameter_types! {
|
|||||||
/// individual routers.
|
/// individual routers.
|
||||||
pub type XcmRouter = (
|
pub type XcmRouter = (
|
||||||
// Only one router so far - use DMP to communicate with child parachains.
|
// 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! {
|
parameter_types! {
|
||||||
pub const RococoForTick: (MultiAsset, MultiLocation) =
|
pub const Rococo: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(RocLocation::get()) });
|
||||||
(AllConcreteFungible { id: Null }, X1(Parachain(100)));
|
pub const RococoForTick: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(100)));
|
||||||
pub const RococoForTrick: (MultiAsset, MultiLocation) =
|
pub const RococoForTrick: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(110)));
|
||||||
(AllConcreteFungible { id: Null }, X1(Parachain(110)));
|
pub const RococoForTrack: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(120)));
|
||||||
pub const RococoForTrack: (MultiAsset, MultiLocation) =
|
pub const RococoForStatemint: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(1001)));
|
||||||
(AllConcreteFungible { id: Null }, X1(Parachain(120)));
|
|
||||||
pub const RococoForStatemint: (MultiAsset, MultiLocation) =
|
|
||||||
(AllConcreteFungible { id: Null }, X1(Parachain(1001)));
|
|
||||||
}
|
}
|
||||||
pub type TrustedTeleporters = (
|
pub type TrustedTeleporters = (
|
||||||
xcm_builder::Case<RococoForTick>,
|
xcm_builder::Case<RococoForTick>,
|
||||||
@@ -692,65 +683,14 @@ pub type LocalOriginToLocation = (
|
|||||||
SignedToAccountId32<Origin, AccountId, RococoNetwork>,
|
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 {
|
impl pallet_xcm::Config for Runtime {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
||||||
type XcmRouter = XcmRouter;
|
type XcmRouter = XcmRouter;
|
||||||
// Anyone can execute XCM messages locally...
|
// Anyone can execute XCM messages locally...
|
||||||
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
||||||
// ...but they must match our filter, which requires them to be a simple withdraw + teleport.
|
// ...but they must match our filter, which right now rejects everything.
|
||||||
type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts;
|
type XcmExecuteFilter = ();
|
||||||
type XcmExecutor = XcmExecutor<XcmConfig>;
|
type XcmExecutor = XcmExecutor<XcmConfig>;
|
||||||
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
|
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
|
||||||
type XcmReserveTransferFilter = All<(MultiLocation, Vec<MultiAsset>)>;
|
type XcmReserveTransferFilter = All<(MultiLocation, Vec<MultiAsset>)>;
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ pub mod fee {
|
|||||||
use frame_support::weights::{
|
use frame_support::weights::{
|
||||||
WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial,
|
WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial,
|
||||||
};
|
};
|
||||||
use primitives::v0::Balance;
|
use primitives::v1::Balance;
|
||||||
use runtime_common::ExtrinsicBaseWeight;
|
use runtime_common::ExtrinsicBaseWeight;
|
||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
pub use sp_runtime::Perbill;
|
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,
|
session_info as parachains_session_info, shared as parachains_shared, ump as parachains_ump,
|
||||||
};
|
};
|
||||||
|
|
||||||
use xcm::v0::{
|
use xcm::latest::prelude::*;
|
||||||
Junction::Parachain,
|
|
||||||
MultiAsset::{self, AllConcreteFungible},
|
|
||||||
MultiLocation::{self, Null, X1},
|
|
||||||
NetworkId, Xcm,
|
|
||||||
};
|
|
||||||
use xcm_builder::{
|
use xcm_builder::{
|
||||||
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom,
|
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom,
|
||||||
ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
|
ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
|
||||||
@@ -871,8 +866,8 @@ impl auctions::Config for Runtime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const WndLocation: MultiLocation = MultiLocation::Null;
|
pub const WndLocation: MultiLocation = MultiLocation::Here;
|
||||||
pub const Ancestry: MultiLocation = MultiLocation::Null;
|
pub const Ancestry: MultiLocation = MultiLocation::Here;
|
||||||
pub WestendNetwork: NetworkId = NetworkId::Named(b"Westend".to_vec());
|
pub WestendNetwork: NetworkId = NetworkId::Named(b"Westend".to_vec());
|
||||||
pub CheckAccount: AccountId = XcmPallet::check_account();
|
pub CheckAccount: AccountId = XcmPallet::check_account();
|
||||||
}
|
}
|
||||||
@@ -908,12 +903,12 @@ parameter_types! {
|
|||||||
/// individual routers.
|
/// individual routers.
|
||||||
pub type XcmRouter = (
|
pub type XcmRouter = (
|
||||||
// Only one router so far - use DMP to communicate with child parachains.
|
// Only one router so far - use DMP to communicate with child parachains.
|
||||||
xcm_sender::ChildParachainRouter<Runtime>,
|
xcm_sender::ChildParachainRouter<Runtime, xcm::AlwaysRelease>,
|
||||||
);
|
);
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const WestendForWestmint: (MultiAsset, MultiLocation) =
|
pub const WestendForWestmint: (MultiAssetFilter, MultiLocation) =
|
||||||
(AllConcreteFungible { id: Null }, X1(Parachain(1000)));
|
(Wild(AllOf { fun: WildFungible, id: Concrete(WndLocation::get()) }), X1(Parachain(1000)));
|
||||||
}
|
}
|
||||||
pub type TrustedTeleporters = (xcm_builder::Case<WestendForWestmint>,);
|
pub type TrustedTeleporters = (xcm_builder::Case<WestendForWestmint>,);
|
||||||
|
|
||||||
@@ -949,65 +944,14 @@ pub type LocalOriginToLocation = (
|
|||||||
SignedToAccountId32<Origin, AccountId, WestendNetwork>,
|
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 {
|
impl pallet_xcm::Config for Runtime {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
||||||
type XcmRouter = XcmRouter;
|
type XcmRouter = XcmRouter;
|
||||||
// Anyone can execute XCM messages locally...
|
// Anyone can execute XCM messages locally...
|
||||||
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
||||||
// ...but they must match our filter, which requires them to be a simple withdraw + teleport.
|
// ...but they must match our filter, which rejects everything.
|
||||||
type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts;
|
type XcmExecuteFilter = ();
|
||||||
type XcmExecutor = XcmExecutor<XcmConfig>;
|
type XcmExecutor = XcmExecutor<XcmConfig>;
|
||||||
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
|
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
|
||||||
type XcmReserveTransferFilter = All<(MultiLocation, Vec<MultiAsset>)>;
|
type XcmReserveTransferFilter = All<(MultiLocation, Vec<MultiAsset>)>;
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ fedora/M
|
|||||||
finalize/B
|
finalize/B
|
||||||
FRAME/MS
|
FRAME/MS
|
||||||
FSMs
|
FSMs
|
||||||
|
fungibility
|
||||||
gameable
|
gameable
|
||||||
getter/MS
|
getter/MS
|
||||||
GiB/S
|
GiB/S
|
||||||
|
|||||||
@@ -23,10 +23,11 @@ mod mock;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
use codec::{Decode, Encode};
|
||||||
use frame_support::traits::{Contains, EnsureOrigin, Filter, Get, OriginTrait};
|
use frame_support::traits::{Contains, EnsureOrigin, Filter, Get, OriginTrait};
|
||||||
use sp_runtime::{traits::BadOrigin, RuntimeDebug};
|
use sp_runtime::{traits::BadOrigin, RuntimeDebug};
|
||||||
use sp_std::{boxed::Box, convert::TryInto, marker::PhantomData, prelude::*, vec};
|
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 xcm_executor::traits::ConvertOrigin;
|
||||||
|
|
||||||
use frame_support::PalletId;
|
use frame_support::PalletId;
|
||||||
@@ -87,7 +88,7 @@ pub mod pallet {
|
|||||||
#[pallet::event]
|
#[pallet::event]
|
||||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||||
pub enum Event<T: Config> {
|
pub enum Event<T: Config> {
|
||||||
Attempted(xcm::v0::Outcome),
|
Attempted(xcm::latest::Outcome),
|
||||||
Sent(MultiLocation, MultiLocation, Xcm<()>),
|
Sent(MultiLocation, MultiLocation, Xcm<()>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,15 +135,15 @@ pub mod pallet {
|
|||||||
/// from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain.
|
/// 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
|
/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be
|
||||||
/// an `AccountId32` value.
|
/// an `AccountId32` value.
|
||||||
/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the
|
/// - `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the
|
||||||
/// `dest` side.
|
/// `dest` side. May not be empty.
|
||||||
/// - `dest_weight`: Equal to the total weight on `dest` of the XCM message
|
/// - `dest_weight`: Equal to the total weight on `dest` of the XCM message
|
||||||
/// `Teleport { assets, effects: [ BuyExecution{..}, DepositAsset{..} ] }`.
|
/// `Teleport { assets, effects: [ BuyExecution{..}, DepositAsset{..} ] }`.
|
||||||
#[pallet::weight({
|
#[pallet::weight({
|
||||||
let mut message = Xcm::WithdrawAsset {
|
let mut message = Xcm::WithdrawAsset {
|
||||||
assets: assets.clone(),
|
assets: assets.clone(),
|
||||||
effects: sp_std::vec![ InitiateTeleport {
|
effects: sp_std::vec![ InitiateTeleport {
|
||||||
assets: sp_std::vec![ All ],
|
assets: Wild(All),
|
||||||
dest: dest.clone(),
|
dest: dest.clone(),
|
||||||
effects: sp_std::vec![],
|
effects: sp_std::vec![],
|
||||||
} ]
|
} ]
|
||||||
@@ -153,21 +154,28 @@ pub mod pallet {
|
|||||||
origin: OriginFor<T>,
|
origin: OriginFor<T>,
|
||||||
dest: MultiLocation,
|
dest: MultiLocation,
|
||||||
beneficiary: MultiLocation,
|
beneficiary: MultiLocation,
|
||||||
assets: Vec<MultiAsset>,
|
assets: MultiAssets,
|
||||||
|
fee_asset_item: u32,
|
||||||
dest_weight: Weight,
|
dest_weight: Weight,
|
||||||
) -> DispatchResult {
|
) -> DispatchResult {
|
||||||
let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
|
let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
|
||||||
ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
|
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);
|
ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
|
||||||
let (origin_location, assets) = value;
|
let (origin_location, assets) = value;
|
||||||
let inv_dest = T::LocationInverter::invert_location(&dest);
|
let inv_dest = T::LocationInverter::invert_location(&dest);
|
||||||
let mut fees = assets.first().ok_or(Error::<T>::Empty)?.clone();
|
let fees = assets
|
||||||
fees.reanchor(&inv_dest).map_err(|_| Error::<T>::CannotReanchor)?;
|
.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 {
|
let mut message = Xcm::WithdrawAsset {
|
||||||
assets,
|
assets,
|
||||||
effects: vec![InitiateTeleport {
|
effects: vec![InitiateTeleport {
|
||||||
assets: vec![All],
|
assets: Wild(All),
|
||||||
dest,
|
dest,
|
||||||
effects: vec![
|
effects: vec![
|
||||||
BuyExecution {
|
BuyExecution {
|
||||||
@@ -176,9 +184,10 @@ pub mod pallet {
|
|||||||
weight: 0,
|
weight: 0,
|
||||||
debt: dest_weight,
|
debt: dest_weight,
|
||||||
halt_on_error: false,
|
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
|
/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the
|
||||||
/// `dest` side.
|
/// `dest` side.
|
||||||
/// - `dest_weight`: Equal to the total weight on `dest` of the XCM message
|
/// - `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({
|
#[pallet::weight({
|
||||||
let mut message = Xcm::TransferReserveAsset {
|
let mut message = Xcm::TransferReserveAsset {
|
||||||
assets: assets.clone(),
|
assets: assets.clone(),
|
||||||
@@ -216,30 +225,38 @@ pub mod pallet {
|
|||||||
origin: OriginFor<T>,
|
origin: OriginFor<T>,
|
||||||
dest: MultiLocation,
|
dest: MultiLocation,
|
||||||
beneficiary: MultiLocation,
|
beneficiary: MultiLocation,
|
||||||
assets: Vec<MultiAsset>,
|
assets: MultiAssets,
|
||||||
|
fee_asset_item: u32,
|
||||||
dest_weight: Weight,
|
dest_weight: Weight,
|
||||||
) -> DispatchResult {
|
) -> DispatchResult {
|
||||||
let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
|
let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
|
||||||
ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
|
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);
|
ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
|
||||||
let (origin_location, assets) = value;
|
let (origin_location, assets) = value;
|
||||||
let inv_dest = T::LocationInverter::invert_location(&dest);
|
let inv_dest = T::LocationInverter::invert_location(&dest);
|
||||||
let mut fees = assets.first().ok_or(Error::<T>::Empty)?.clone();
|
let fees = assets
|
||||||
fees.reanchor(&inv_dest).map_err(|_| Error::<T>::CannotReanchor)?;
|
.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 {
|
let mut message = Xcm::TransferReserveAsset {
|
||||||
assets,
|
assets,
|
||||||
dest,
|
dest,
|
||||||
effects: vec![
|
effects: vec![
|
||||||
BuyExecution {
|
BuyExecution {
|
||||||
fees,
|
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,
|
weight: 0,
|
||||||
debt: dest_weight,
|
debt: dest_weight, // covers this, `TransferReserveAsset` xcm, and `DepositAsset` order.
|
||||||
halt_on_error: false,
|
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 =
|
let weight =
|
||||||
@@ -286,7 +303,7 @@ pub mod pallet {
|
|||||||
message: Xcm<()>,
|
message: Xcm<()>,
|
||||||
) -> Result<(), XcmError> {
|
) -> Result<(), XcmError> {
|
||||||
let message = match interior {
|
let message = match interior {
|
||||||
MultiLocation::Null => message,
|
MultiLocation::Here => message,
|
||||||
who => Xcm::<()>::RelayedFrom { who, message: Box::new(message) },
|
who => Xcm::<()>::RelayedFrom { who, message: Box::new(message) },
|
||||||
};
|
};
|
||||||
log::trace!(target: "xcm::send_xcm", "dest: {:?}, message: {:?}", &dest, &message);
|
log::trace!(target: "xcm::send_xcm", "dest: {:?}, message: {:?}", &dest, &message);
|
||||||
@@ -363,7 +380,7 @@ where
|
|||||||
|
|
||||||
#[cfg(feature = "runtime-benchmarks")]
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
fn successful_origin() -> O {
|
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};
|
use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32};
|
||||||
pub use sp_std::{cell::RefCell, fmt::Debug, marker::PhantomData};
|
pub use sp_std::{cell::RefCell, fmt::Debug, marker::PhantomData};
|
||||||
use xcm::{
|
use xcm::{
|
||||||
opaque::v0::{Error as XcmError, MultiAsset, Result as XcmResult, SendXcm, Xcm},
|
latest::prelude::*,
|
||||||
v0::{MultiLocation, NetworkId, Order},
|
opaque::latest::{Error as XcmError, MultiAsset, Result as XcmResult, SendXcm, Xcm},
|
||||||
};
|
};
|
||||||
use xcm_builder::{
|
use xcm_builder::{
|
||||||
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative,
|
AccountId32Aliases, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative,
|
||||||
ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
|
ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
|
||||||
CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfConcreteFungible, FixedWeightBounds,
|
CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete,
|
||||||
IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32,
|
LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation,
|
||||||
SovereignSignedViaLocation, TakeWeightCredit,
|
TakeWeightCredit,
|
||||||
};
|
};
|
||||||
use xcm_executor::XcmExecutor;
|
use xcm_executor::XcmExecutor;
|
||||||
|
|
||||||
@@ -133,9 +133,9 @@ impl pallet_balances::Config for Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const RelayLocation: MultiLocation = MultiLocation::Null;
|
pub const RelayLocation: MultiLocation = MultiLocation::Here;
|
||||||
pub const AnyNetwork: NetworkId = NetworkId::Any;
|
pub const AnyNetwork: NetworkId = NetworkId::Any;
|
||||||
pub Ancestry: MultiLocation = MultiLocation::Null;
|
pub Ancestry: MultiLocation = MultiLocation::Here;
|
||||||
pub UnitWeightCost: Weight = 1_000;
|
pub UnitWeightCost: Weight = 1_000;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ type LocalOriginConverter = (
|
|||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const BaseXcmWeight: Weight = 1_000;
|
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>>);
|
pub type Barrier = (TakeWeightCredit, AllowTopLevelPaidExecutionFrom<All<MultiLocation>>);
|
||||||
@@ -170,7 +170,7 @@ impl xcm_executor::Config for XcmConfig {
|
|||||||
type LocationInverter = LocationInverter<Ancestry>;
|
type LocationInverter = LocationInverter<Ancestry>;
|
||||||
type Barrier = Barrier;
|
type Barrier = Barrier;
|
||||||
type Weigher = FixedWeightBounds<BaseXcmWeight, Call>;
|
type Weigher = FixedWeightBounds<BaseXcmWeight, Call>;
|
||||||
type Trader = FixedRateOfConcreteFungible<CurrencyPerSecond, ()>;
|
type Trader = FixedRateOfFungible<CurrencyPerSecond, ()>;
|
||||||
type ResponseHandler = ();
|
type ResponseHandler = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ impl pallet_xcm::Config for Test {
|
|||||||
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
||||||
type XcmRouter = (TestSendXcmErrX8, TestSendXcm);
|
type XcmRouter = (TestSendXcmErrX8, TestSendXcm);
|
||||||
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<Origin, LocalOriginToLocation>;
|
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 XcmExecutor = XcmExecutor<XcmConfig>;
|
||||||
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
|
type XcmTeleportFilter = All<(MultiLocation, Vec<MultiAsset>)>;
|
||||||
type XcmReserveTransferFilter = 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
|
System::events().pop().expect("Event expected").event
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn buy_execution<C>(debt: Weight, fees: MultiAsset) -> Order<C> {
|
pub(crate) fn buy_execution<C>(fees: impl Into<MultiAsset>, debt: Weight) -> Order<C> {
|
||||||
use xcm::opaque::v0::prelude::*;
|
use xcm::opaque::latest::prelude::*;
|
||||||
Order::BuyExecution { fees, weight: 0, debt, halt_on_error: false, xcm: vec![] }
|
Order::BuyExecution {
|
||||||
|
fees: fees.into(),
|
||||||
|
weight: 0,
|
||||||
|
debt,
|
||||||
|
halt_on_error: false,
|
||||||
|
orders: vec![],
|
||||||
|
instructions: vec![],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_test_ext_with_balances(
|
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 frame_support::{assert_noop, assert_ok, traits::Currency};
|
||||||
use polkadot_parachain::primitives::{AccountIdConversion, Id as ParaId};
|
use polkadot_parachain::primitives::{AccountIdConversion, Id as ParaId};
|
||||||
use xcm::{
|
use xcm::{
|
||||||
opaque::v0::prelude::*,
|
opaque::v1::prelude::*,
|
||||||
v0::{Junction, Xcm},
|
v1::{Junction, Xcm},
|
||||||
};
|
};
|
||||||
|
|
||||||
const ALICE: AccountId = AccountId::new([0u8; 32]);
|
const ALICE: AccountId = AccountId::new([0u8; 32]);
|
||||||
@@ -38,22 +38,19 @@ fn send_works() {
|
|||||||
new_test_ext_with_balances(balances).execute_with(|| {
|
new_test_ext_with_balances(balances).execute_with(|| {
|
||||||
let weight = 2 * BaseXcmWeight::get();
|
let weight = 2 * BaseXcmWeight::get();
|
||||||
let sender: MultiLocation =
|
let sender: MultiLocation =
|
||||||
Junction::AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into();
|
AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into();
|
||||||
let message = Xcm::ReserveAssetDeposit {
|
let message = Xcm::ReserveAssetDeposited {
|
||||||
assets: vec![ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT }],
|
assets: (X1(Parent), SEND_AMOUNT).into(),
|
||||||
effects: vec![
|
effects: vec![
|
||||||
buy_execution(
|
buy_execution((Parent, SEND_AMOUNT), weight),
|
||||||
weight,
|
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() },
|
||||||
ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT },
|
|
||||||
),
|
|
||||||
DepositAsset { assets: vec![All], dest: sender.clone() },
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
assert_ok!(XcmPallet::send(Origin::signed(ALICE), RelayLocation::get(), message.clone()));
|
assert_ok!(XcmPallet::send(Origin::signed(ALICE), RelayLocation::get(), message.clone()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sent_xcm(),
|
sent_xcm(),
|
||||||
vec![(
|
vec![(
|
||||||
MultiLocation::Null,
|
MultiLocation::Here,
|
||||||
RelayedFrom { who: sender.clone(), message: Box::new(message.clone()) }
|
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 weight = 2 * BaseXcmWeight::get();
|
||||||
let sender: MultiLocation =
|
let sender: MultiLocation =
|
||||||
Junction::AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into();
|
Junction::AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into();
|
||||||
let message = Xcm::ReserveAssetDeposit {
|
let message = Xcm::ReserveAssetDeposited {
|
||||||
assets: vec![ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT }],
|
assets: (Parent, SEND_AMOUNT).into(),
|
||||||
effects: vec![
|
effects: vec![
|
||||||
buy_execution(
|
buy_execution((Parent, SEND_AMOUNT), weight),
|
||||||
weight,
|
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() },
|
||||||
ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT },
|
|
||||||
),
|
|
||||||
DepositAsset { assets: vec![All], dest: sender.clone() },
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
assert_noop!(
|
assert_noop!(
|
||||||
@@ -120,8 +114,9 @@ fn teleport_assets_works() {
|
|||||||
assert_ok!(XcmPallet::teleport_assets(
|
assert_ok!(XcmPallet::teleport_assets(
|
||||||
Origin::signed(ALICE),
|
Origin::signed(ALICE),
|
||||||
RelayLocation::get(),
|
RelayLocation::get(),
|
||||||
MultiLocation::X1(Junction::AccountId32 { network: NetworkId::Any, id: BOB.into() }),
|
X1(AccountId32 { network: Any, id: BOB.into() }),
|
||||||
vec![ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT }],
|
(Here, SEND_AMOUNT).into(),
|
||||||
|
0,
|
||||||
weight,
|
weight,
|
||||||
));
|
));
|
||||||
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT);
|
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT);
|
||||||
@@ -149,7 +144,8 @@ fn reserve_transfer_assets_works() {
|
|||||||
Origin::signed(ALICE),
|
Origin::signed(ALICE),
|
||||||
Parachain(PARA_ID).into(),
|
Parachain(PARA_ID).into(),
|
||||||
dest.clone(),
|
dest.clone(),
|
||||||
vec![ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT }],
|
(Here, SEND_AMOUNT).into(),
|
||||||
|
0,
|
||||||
weight
|
weight
|
||||||
));
|
));
|
||||||
// Alice spent amount
|
// Alice spent amount
|
||||||
@@ -161,14 +157,11 @@ fn reserve_transfer_assets_works() {
|
|||||||
sent_xcm(),
|
sent_xcm(),
|
||||||
vec![(
|
vec![(
|
||||||
Parachain(PARA_ID).into(),
|
Parachain(PARA_ID).into(),
|
||||||
Xcm::ReserveAssetDeposit {
|
Xcm::ReserveAssetDeposited {
|
||||||
assets: vec![ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT }],
|
assets: (X1(Parent), SEND_AMOUNT).into(),
|
||||||
effects: vec![
|
effects: vec![
|
||||||
buy_execution(
|
buy_execution((Parent, SEND_AMOUNT), weight),
|
||||||
weight,
|
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest },
|
||||||
ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT }
|
|
||||||
),
|
|
||||||
DepositAsset { assets: vec![All], dest },
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
)]
|
)]
|
||||||
@@ -196,13 +189,10 @@ fn execute_withdraw_to_deposit_works() {
|
|||||||
assert_ok!(XcmPallet::execute(
|
assert_ok!(XcmPallet::execute(
|
||||||
Origin::signed(ALICE),
|
Origin::signed(ALICE),
|
||||||
Box::new(Xcm::WithdrawAsset {
|
Box::new(Xcm::WithdrawAsset {
|
||||||
assets: vec![ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT }],
|
assets: (Here, SEND_AMOUNT).into(),
|
||||||
effects: vec![
|
effects: vec![
|
||||||
buy_execution(
|
buy_execution((Here, SEND_AMOUNT), weight),
|
||||||
weight,
|
DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest }
|
||||||
ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT }
|
|
||||||
),
|
|
||||||
DepositAsset { assets: vec![All], dest },
|
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
weight
|
weight
|
||||||
|
|||||||
+108
-13
@@ -23,14 +23,32 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
use core::{
|
||||||
|
convert::{TryFrom, TryInto},
|
||||||
|
result::Result,
|
||||||
|
};
|
||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
use parity_scale_codec::{Decode, Encode};
|
use parity_scale_codec::{Decode, Encode, Error as CodecError, Input};
|
||||||
|
|
||||||
pub mod v0;
|
pub mod v0;
|
||||||
|
pub mod v1;
|
||||||
|
|
||||||
|
pub mod latest {
|
||||||
|
pub use super::v1::*;
|
||||||
|
}
|
||||||
|
|
||||||
mod double_encoded;
|
mod double_encoded;
|
||||||
pub use double_encoded::DoubleEncoded;
|
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.
|
/// A single XCM message, together with its version code.
|
||||||
#[derive(Derivative, Encode, Decode)]
|
#[derive(Derivative, Encode, Decode)]
|
||||||
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
|
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
|
||||||
@@ -38,8 +56,87 @@ pub use double_encoded::DoubleEncoded;
|
|||||||
#[codec(decode_bound())]
|
#[codec(decode_bound())]
|
||||||
pub enum VersionedXcm<Call> {
|
pub enum VersionedXcm<Call> {
|
||||||
V0(v0::Xcm<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 opaque {
|
||||||
pub mod v0 {
|
pub mod v0 {
|
||||||
// Everything from v0
|
// Everything from v0
|
||||||
@@ -47,19 +144,17 @@ pub mod opaque {
|
|||||||
// Then override with the opaque types in v0
|
// Then override with the opaque types in v0
|
||||||
pub use crate::v0::opaque::{Order, Xcm};
|
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.
|
/// The basic `VersionedXcm` type which just uses the `Vec<u8>` as an encoded call.
|
||||||
pub type VersionedXcm = super::VersionedXcm<()>;
|
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.
|
//! Version 0 of the Cross-Consensus Message format data structures.
|
||||||
|
|
||||||
use crate::{DoubleEncoded, VersionedMultiAsset, VersionedXcm};
|
use crate::DoubleEncoded;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::{convert::TryFrom, fmt::Debug, result};
|
use core::{
|
||||||
|
convert::{TryFrom, TryInto},
|
||||||
|
result,
|
||||||
|
};
|
||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
use parity_scale_codec::{self, Decode, Encode};
|
use parity_scale_codec::{self, Decode, Encode};
|
||||||
|
|
||||||
mod junction;
|
|
||||||
mod multi_asset;
|
mod multi_asset;
|
||||||
mod multi_location;
|
mod multi_location;
|
||||||
mod order;
|
mod order;
|
||||||
mod traits;
|
mod traits;
|
||||||
pub use junction::{BodyId, BodyPart, Junction, NetworkId};
|
use super::v1::Xcm as Xcm1;
|
||||||
pub use multi_asset::{AssetInstance, MultiAsset};
|
pub use multi_asset::{AssetInstance, MultiAsset};
|
||||||
pub use multi_location::MultiLocation;
|
pub use multi_location::MultiLocation;
|
||||||
pub use order::Order;
|
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.
|
/// A prelude for importing all types typically used when interacting with XCM messages.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use super::{
|
pub use super::{
|
||||||
junction::{BodyId, BodyPart, Junction::*, NetworkId},
|
|
||||||
multi_asset::{
|
multi_asset::{
|
||||||
AssetInstance::{self, *},
|
AssetInstance::{self, *},
|
||||||
MultiAsset::{self, *},
|
MultiAsset::{self, *},
|
||||||
@@ -44,43 +45,14 @@ pub mod prelude {
|
|||||||
multi_location::MultiLocation::{self, *},
|
multi_location::MultiLocation::{self, *},
|
||||||
order::Order::{self, *},
|
order::Order::{self, *},
|
||||||
traits::{Error as XcmError, ExecuteXcm, Outcome, Result as XcmResult, SendXcm},
|
traits::{Error as XcmError, ExecuteXcm, Outcome, Result as XcmResult, SendXcm},
|
||||||
OriginKind,
|
BodyId, BodyPart,
|
||||||
|
Junction::*,
|
||||||
|
NetworkId, OriginKind,
|
||||||
Xcm::{self, *},
|
Xcm::{self, *},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: #2841 #XCMENCODE Efficient encodings for Vec<MultiAsset>, Vec<Order>, using initial byte values 128+ to encode
|
pub use super::v1::{BodyId, BodyPart, Junction, NetworkId, OriginKind, Response};
|
||||||
// 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>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cross-Consensus Message: A message from one consensus system to another.
|
/// 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>> },
|
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> {
|
impl<Call> Xcm<Call> {
|
||||||
pub fn into<C>(self) -> Xcm<C> {
|
pub fn into<C>(self) -> Xcm<C> {
|
||||||
Xcm::from(self)
|
Xcm::from(self)
|
||||||
@@ -329,3 +286,56 @@ pub mod opaque {
|
|||||||
|
|
||||||
pub use super::order::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.
|
//! Cross-Consensus Message format data structures.
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use super::MultiLocation;
|
||||||
use core::{convert::TryFrom, result};
|
use crate::v1::{MultiAsset as MultiAsset1, MultiAssetFilter, MultiAssets, WildMultiAsset};
|
||||||
|
use alloc::{vec, vec::Vec};
|
||||||
use super::{MultiLocation, VersionedMultiAsset};
|
|
||||||
use parity_scale_codec::{self, Decode, Encode};
|
use parity_scale_codec::{self, Decode, Encode};
|
||||||
|
|
||||||
/// A general identifier for an instance of a non-fungible asset class.
|
pub use crate::v1::AssetInstance;
|
||||||
#[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>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A single general identifier for an asset.
|
/// A single general identifier for an asset.
|
||||||
///
|
///
|
||||||
@@ -318,17 +290,53 @@ impl MultiAsset {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<MultiAsset> for VersionedMultiAsset {
|
impl From<MultiAsset1> for MultiAsset {
|
||||||
fn from(x: MultiAsset) -> Self {
|
fn from(a: MultiAsset1) -> MultiAsset {
|
||||||
VersionedMultiAsset::V0(x)
|
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 {
|
impl From<MultiAssets> for Vec<MultiAsset> {
|
||||||
type Error = ();
|
fn from(a: MultiAssets) -> Vec<MultiAsset> {
|
||||||
fn try_from(x: VersionedMultiAsset) -> result::Result<Self, ()> {
|
a.drain().into_iter().map(MultiAsset::from).collect()
|
||||||
match x {
|
}
|
||||||
VersionedMultiAsset::V0(x) => Ok(x),
|
}
|
||||||
|
|
||||||
|
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 }));
|
.contains(&AbstractFungible { id: vec![99u8], amount: 100 }));
|
||||||
|
|
||||||
// For non-fungibles, containing is equality.
|
// For non-fungibles, containing is equality.
|
||||||
assert!(!AbstractNonFungible {
|
assert!(!AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(9) }
|
||||||
class: vec![99u8],
|
.contains(&AbstractNonFungible {
|
||||||
instance: AssetInstance::Index { id: 9 }
|
class: vec![98u8],
|
||||||
}
|
instance: AssetInstance::Index(9)
|
||||||
.contains(&AbstractNonFungible {
|
}));
|
||||||
class: vec![98u8],
|
assert!(!AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(8) }
|
||||||
instance: AssetInstance::Index { id: 9 }
|
.contains(&AbstractNonFungible {
|
||||||
}));
|
class: vec![99u8],
|
||||||
assert!(!AbstractNonFungible {
|
instance: AssetInstance::Index(9)
|
||||||
class: vec![99u8],
|
}));
|
||||||
instance: AssetInstance::Index { id: 8 }
|
assert!(AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(9) }
|
||||||
}
|
.contains(&AbstractNonFungible {
|
||||||
.contains(&AbstractNonFungible {
|
class: vec![99u8],
|
||||||
class: vec![99u8],
|
instance: AssetInstance::Index(9)
|
||||||
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 }
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,9 @@
|
|||||||
|
|
||||||
//! Cross-Consensus Message format data structures.
|
//! Cross-Consensus Message format data structures.
|
||||||
|
|
||||||
use core::{convert::TryFrom, mem, result};
|
use core::{mem, result};
|
||||||
|
|
||||||
use super::Junction;
|
use super::{super::v1::MultiLocation as MultiLocation1, Junction};
|
||||||
use crate::VersionedMultiLocation;
|
|
||||||
use parity_scale_codec::{self, Decode, Encode};
|
use parity_scale_codec::{self, Decode, Encode};
|
||||||
|
|
||||||
/// A relative path between state-bearing consensus systems.
|
/// A relative path between state-bearing consensus systems.
|
||||||
@@ -697,17 +696,20 @@ impl MultiLocation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<MultiLocation> for VersionedMultiLocation {
|
impl From<MultiLocation1> for MultiLocation {
|
||||||
fn from(x: MultiLocation) -> Self {
|
fn from(old: MultiLocation1) -> Self {
|
||||||
VersionedMultiLocation::V0(x)
|
use MultiLocation::*;
|
||||||
}
|
match old {
|
||||||
}
|
MultiLocation1::Here => Null,
|
||||||
|
MultiLocation1::X1(j0) => X1(j0),
|
||||||
impl TryFrom<VersionedMultiLocation> for MultiLocation {
|
MultiLocation1::X2(j0, j1) => X2(j0, j1),
|
||||||
type Error = ();
|
MultiLocation1::X3(j0, j1, j2) => X3(j0, j1, j2),
|
||||||
fn try_from(x: VersionedMultiLocation) -> result::Result<Self, ()> {
|
MultiLocation1::X4(j0, j1, j2, j3) => X4(j0, j1, j2, j3),
|
||||||
match x {
|
MultiLocation1::X5(j0, j1, j2, j3, j4) => X5(j0, j1, j2, j3, j4),
|
||||||
VersionedMultiLocation::V0(x) => Ok(x),
|
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.
|
//! 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 alloc::vec::Vec;
|
||||||
|
use core::{convert::TryFrom, result};
|
||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
use parity_scale_codec::{self, Decode, Encode};
|
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.
|
/// A named body.
|
||||||
Named(Vec<u8>),
|
Named(Vec<u8>),
|
||||||
/// An indexed body.
|
/// An indexed body.
|
||||||
// TODO: parity-scale-codec#262: Change to be a tuple.
|
Index(#[codec(compact)] u32),
|
||||||
Index {
|
|
||||||
#[codec(compact)]
|
|
||||||
id: u32,
|
|
||||||
},
|
|
||||||
/// The unambiguous executive body (for Polkadot, this would be the Polkadot council).
|
/// The unambiguous executive body (for Polkadot, this would be the Polkadot council).
|
||||||
Executive,
|
Executive,
|
||||||
/// The unambiguous technical body (for Polkadot, this would be the Technical Committee).
|
/// 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 frame_support::{ensure, traits::Contains, weights::Weight};
|
||||||
use polkadot_parachain::primitives::IsSystem;
|
use polkadot_parachain::primitives::IsSystem;
|
||||||
use sp_std::{marker::PhantomData, result::Result};
|
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};
|
use xcm_executor::traits::{OnResponse, ShouldExecute};
|
||||||
|
|
||||||
/// Execution barrier that just takes `shallow_weight` from `weight_credit`.
|
/// 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!(T::contains(origin), ());
|
||||||
ensure!(top_level, ());
|
ensure!(top_level, ());
|
||||||
match message {
|
match message {
|
||||||
Xcm::TeleportAsset { effects, .. } |
|
Xcm::ReceiveTeleportedAsset { effects, .. } |
|
||||||
Xcm::WithdrawAsset { effects, .. } |
|
Xcm::WithdrawAsset { effects, .. } |
|
||||||
Xcm::ReserveAssetDeposit { effects, .. }
|
Xcm::ReserveAssetDeposited { effects, .. }
|
||||||
if matches!(
|
if matches!(
|
||||||
effects.first(),
|
effects.first(),
|
||||||
Some(Order::BuyExecution { debt, ..}) if *debt >= shallow_weight
|
Some(Order::BuyExecution { debt, ..}) if *debt >= shallow_weight
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
use frame_support::traits::{ExistenceRequirement::AllowDeath, Get, WithdrawReasons};
|
use frame_support::traits::{ExistenceRequirement::AllowDeath, Get, WithdrawReasons};
|
||||||
use sp_runtime::traits::{CheckedSub, SaturatedConversion};
|
use sp_runtime::traits::{CheckedSub, SaturatedConversion};
|
||||||
use sp_std::{convert::TryInto, marker::PhantomData, result};
|
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::{
|
use xcm_executor::{
|
||||||
traits::{Convert, MatchesFungible, TransactAsset},
|
traits::{Convert, MatchesFungible, TransactAsset},
|
||||||
Assets,
|
Assets,
|
||||||
@@ -53,7 +53,7 @@ impl From<Error> for XcmError {
|
|||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// use frame_support::parameter_types;
|
/// use frame_support::parameter_types;
|
||||||
/// use xcm::v0::{MultiLocation, Junction};
|
/// use xcm::latest::{MultiLocation, Junction};
|
||||||
/// use xcm_builder::{ParentIsDefault, CurrencyAdapter, IsConcrete};
|
/// use xcm_builder::{ParentIsDefault, CurrencyAdapter, IsConcrete};
|
||||||
///
|
///
|
||||||
/// /// Our chain's account id.
|
/// /// Our chain's account id.
|
||||||
|
|||||||
@@ -18,20 +18,20 @@
|
|||||||
|
|
||||||
use frame_support::traits::Get;
|
use frame_support::traits::Get;
|
||||||
use sp_std::marker::PhantomData;
|
use sp_std::marker::PhantomData;
|
||||||
use xcm::v0::{MultiAsset, MultiLocation};
|
use xcm::latest::{AssetId::Concrete, MultiAsset, MultiAssetFilter, MultiLocation};
|
||||||
use xcm_executor::traits::FilterAssetLocation;
|
use xcm_executor::traits::FilterAssetLocation;
|
||||||
|
|
||||||
/// Accepts an asset iff it is a native asset.
|
/// Accepts an asset iff it is a native asset.
|
||||||
pub struct NativeAsset;
|
pub struct NativeAsset;
|
||||||
impl FilterAssetLocation for NativeAsset {
|
impl FilterAssetLocation for NativeAsset {
|
||||||
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
|
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.
|
/// Accepts an asset if it is contained in the given `T`'s `Get` implementation.
|
||||||
pub struct Case<T>(PhantomData<T>);
|
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 {
|
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
|
||||||
let (a, o) = T::get();
|
let (a, o) = T::get();
|
||||||
a.contains(asset) && &o == origin
|
a.contains(asset) && &o == origin
|
||||||
|
|||||||
@@ -18,7 +18,12 @@
|
|||||||
|
|
||||||
use frame_support::traits::{tokens::fungibles, Contains, Get};
|
use frame_support::traits::{tokens::fungibles, Contains, Get};
|
||||||
use sp_std::{borrow::Borrow, marker::PhantomData, prelude::*, result};
|
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};
|
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
|
/// 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>
|
for ConvertedConcreteAssetId<AssetId, Balance, ConvertAssetId, ConvertBalance>
|
||||||
{
|
{
|
||||||
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> {
|
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> {
|
||||||
let (id, amount) = match a {
|
let (amount, id) = match (&a.fun, &a.id) {
|
||||||
MultiAsset::ConcreteFungible { id, amount } => (id, amount),
|
(Fungible(ref amount), Concrete(ref id)) => (amount, id),
|
||||||
_ => return Err(MatchError::AssetNotFound),
|
_ => return Err(MatchError::AssetNotFound),
|
||||||
};
|
};
|
||||||
let what =
|
let what =
|
||||||
@@ -85,8 +90,8 @@ impl<
|
|||||||
for ConvertedAbstractAssetId<AssetId, Balance, ConvertAssetId, ConvertBalance>
|
for ConvertedAbstractAssetId<AssetId, Balance, ConvertAssetId, ConvertBalance>
|
||||||
{
|
{
|
||||||
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> {
|
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> {
|
||||||
let (id, amount) = match a {
|
let (amount, id) = match (&a.fun, &a.id) {
|
||||||
MultiAsset::AbstractFungible { id, amount } => (id, amount),
|
(Fungible(ref amount), Abstract(ref id)) => (amount, id),
|
||||||
_ => return Err(MatchError::AssetNotFound),
|
_ => return Err(MatchError::AssetNotFound),
|
||||||
};
|
};
|
||||||
let what =
|
let what =
|
||||||
|
|||||||
@@ -55,7 +55,9 @@ pub use fungibles_adapter::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
mod weight;
|
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;
|
mod matches_fungible;
|
||||||
pub use matches_fungible::{IsAbstract, IsConcrete};
|
pub use matches_fungible::{IsAbstract, IsConcrete};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use parity_scale_codec::Encode;
|
|||||||
use sp_io::hashing::blake2_256;
|
use sp_io::hashing::blake2_256;
|
||||||
use sp_runtime::traits::AccountIdConversion;
|
use sp_runtime::traits::AccountIdConversion;
|
||||||
use sp_std::{borrow::Borrow, marker::PhantomData};
|
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};
|
use xcm_executor::traits::{Convert, InvertLocation};
|
||||||
|
|
||||||
pub struct Account32Hash<Network, AccountId>(PhantomData<(Network, AccountId)>);
|
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
|
/// ```rust
|
||||||
/// # use frame_support::parameter_types;
|
/// # 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_builder::LocationInverter;
|
||||||
/// # use xcm_executor::traits::InvertLocation;
|
/// # use xcm_executor::traits::InvertLocation;
|
||||||
/// # fn main() {
|
/// # fn main() {
|
||||||
@@ -200,7 +200,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use frame_support::parameter_types;
|
use frame_support::parameter_types;
|
||||||
use xcm::v0::{Junction::*, MultiLocation::*, NetworkId::Any};
|
use xcm::latest::{Junction::*, MultiLocation::*, NetworkId::Any};
|
||||||
|
|
||||||
fn account20() -> Junction {
|
fn account20() -> Junction {
|
||||||
AccountKey20 { network: Any, key: Default::default() }
|
AccountKey20 { network: Any, key: Default::default() }
|
||||||
|
|||||||
@@ -19,7 +19,11 @@
|
|||||||
use frame_support::traits::Get;
|
use frame_support::traits::Get;
|
||||||
use sp_runtime::traits::CheckedConversion;
|
use sp_runtime::traits::CheckedConversion;
|
||||||
use sp_std::{convert::TryFrom, marker::PhantomData};
|
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;
|
use xcm_executor::traits::MatchesFungible;
|
||||||
|
|
||||||
/// Converts a `MultiAsset` into balance `B` if it is a concrete fungible with an id equal to that
|
/// 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
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use xcm::v0::{MultiAsset, MultiLocation, Junction};
|
/// use xcm::latest::prelude::*;
|
||||||
/// use xcm_builder::IsConcrete;
|
/// use xcm_builder::IsConcrete;
|
||||||
/// use xcm_executor::traits::MatchesFungible;
|
/// use xcm_executor::traits::MatchesFungible;
|
||||||
///
|
///
|
||||||
/// frame_support::parameter_types! {
|
/// frame_support::parameter_types! {
|
||||||
/// pub TargetLocation: MultiLocation = MultiLocation::X1(Junction::Parent);
|
/// pub TargetLocation: MultiLocation = X1(Parent);
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// # fn main() {
|
/// # fn main() {
|
||||||
/// let id = MultiLocation::X1(Junction::Parent);
|
/// let asset = (X1(Parent), 999).into();
|
||||||
/// let asset = MultiAsset::ConcreteFungible { id, amount: 999u128 };
|
|
||||||
/// // match `asset` if it is a concrete asset in `TargetLocation`.
|
/// // match `asset` if it is a concrete asset in `TargetLocation`.
|
||||||
/// assert_eq!(<IsConcrete<TargetLocation> as MatchesFungible<u128>>::matches_fungible(&asset), Some(999));
|
/// 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>);
|
pub struct IsConcrete<T>(PhantomData<T>);
|
||||||
impl<T: Get<MultiLocation>, B: TryFrom<u128>> MatchesFungible<B> for IsConcrete<T> {
|
impl<T: Get<MultiLocation>, B: TryFrom<u128>> MatchesFungible<B> for IsConcrete<T> {
|
||||||
fn matches_fungible(a: &MultiAsset) -> Option<B> {
|
fn matches_fungible(a: &MultiAsset) -> Option<B> {
|
||||||
match a {
|
match (&a.id, &a.fun) {
|
||||||
MultiAsset::ConcreteFungible { id, amount } if id == &T::get() =>
|
(Concrete(ref id), Fungible(ref amount)) if id == &T::get() =>
|
||||||
CheckedConversion::checked_from(*amount),
|
CheckedConversion::checked_from(*amount),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@@ -59,7 +62,7 @@ impl<T: Get<MultiLocation>, B: TryFrom<u128>> MatchesFungible<B> for IsConcrete<
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use xcm::v0::{MultiAsset};
|
/// use xcm::latest::prelude::*;
|
||||||
/// use xcm_builder::IsAbstract;
|
/// use xcm_builder::IsAbstract;
|
||||||
/// use xcm_executor::traits::MatchesFungible;
|
/// use xcm_executor::traits::MatchesFungible;
|
||||||
///
|
///
|
||||||
@@ -68,7 +71,7 @@ impl<T: Get<MultiLocation>, B: TryFrom<u128>> MatchesFungible<B> for IsConcrete<
|
|||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// # fn main() {
|
/// # 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`.
|
/// // match `asset` if it is a concrete asset in `TargetLocation`.
|
||||||
/// assert_eq!(<IsAbstract<TargetLocation> as MatchesFungible<u128>>::matches_fungible(&asset), Some(999));
|
/// 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>);
|
pub struct IsAbstract<T>(PhantomData<T>);
|
||||||
impl<T: Get<&'static [u8]>, B: TryFrom<u128>> MatchesFungible<B> for IsAbstract<T> {
|
impl<T: Get<&'static [u8]>, B: TryFrom<u128>> MatchesFungible<B> for IsAbstract<T> {
|
||||||
fn matches_fungible(a: &MultiAsset) -> Option<B> {
|
fn matches_fungible(a: &MultiAsset) -> Option<B> {
|
||||||
match a {
|
match (&a.id, &a.fun) {
|
||||||
MultiAsset::AbstractFungible { id, amount } if &id[..] == T::get() =>
|
(Abstract(ref id), Fungible(ref amount)) if id == &T::get() =>
|
||||||
CheckedConversion::checked_from(*amount),
|
CheckedConversion::checked_from(*amount),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
AllowKnownQueryResponses, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom,
|
AllowKnownQueryResponses, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom,
|
||||||
FixedRateOfConcreteFungible, FixedWeightBounds, LocationInverter, TakeWeightCredit,
|
FixedRateOfFungible, FixedWeightBounds, LocationInverter, TakeWeightCredit,
|
||||||
};
|
};
|
||||||
pub use frame_support::{
|
pub use frame_support::{
|
||||||
dispatch::{
|
dispatch::{
|
||||||
@@ -34,10 +34,7 @@ pub use sp_std::{
|
|||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
};
|
};
|
||||||
pub use xcm::v0::{
|
pub use xcm::latest::prelude::*;
|
||||||
opaque, Error as XcmError, Junction, Junction::*, MultiAsset, MultiLocation, MultiLocation::*,
|
|
||||||
Order, OriginKind, Result as XcmResult, SendXcm, Xcm,
|
|
||||||
};
|
|
||||||
pub use xcm_executor::{
|
pub use xcm_executor::{
|
||||||
traits::{ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset},
|
traits::{ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset},
|
||||||
Assets, Config,
|
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()))
|
ASSETS.with(|a| a.borrow().get(&who).map_or(vec![], |a| a.clone().into()))
|
||||||
}
|
}
|
||||||
pub fn add_asset(who: u64, what: MultiAsset) {
|
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;
|
pub struct TestAssetTransactor;
|
||||||
@@ -140,8 +137,8 @@ impl TransactAsset for TestAssetTransactor {
|
|||||||
a.borrow_mut()
|
a.borrow_mut()
|
||||||
.get_mut(&who)
|
.get_mut(&who)
|
||||||
.ok_or(XcmError::NotWithdrawable)?
|
.ok_or(XcmError::NotWithdrawable)?
|
||||||
.try_take(what.clone())
|
.try_take(what.clone().into())
|
||||||
.map_err(|()| XcmError::NotWithdrawable)
|
.map_err(|_| XcmError::NotWithdrawable)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,7 +152,7 @@ pub fn to_account(l: MultiLocation) -> Result<u64, MultiLocation> {
|
|||||||
// Children at 1000+id
|
// Children at 1000+id
|
||||||
X1(Parachain(id)) => 1000 + id as u64,
|
X1(Parachain(id)) => 1000 + id as u64,
|
||||||
// Self at 3000
|
// Self at 3000
|
||||||
Null => 3000,
|
Here => 3000,
|
||||||
// Parent at 3001
|
// Parent at 3001
|
||||||
X1(Parent) => 3001,
|
X1(Parent) => 3001,
|
||||||
l => return Err(l),
|
l => return Err(l),
|
||||||
@@ -181,14 +178,14 @@ impl ConvertOrigin<TestOrigin> for TestOriginConverter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
pub static IS_RESERVE: 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<MultiAsset>>> = 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));
|
IS_RESERVE.with(|r| r.borrow_mut().entry(from).or_default().push(asset));
|
||||||
}
|
}
|
||||||
#[allow(dead_code)]
|
#[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));
|
IS_TELEPORTER.with(|r| r.borrow_mut().entry(from).or_default().push(asset));
|
||||||
}
|
}
|
||||||
pub struct TestIsReserve;
|
pub struct TestIsReserve;
|
||||||
@@ -206,7 +203,7 @@ impl FilterAssetLocation for TestIsTeleporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use xcm::v0::Response;
|
use xcm::latest::Response;
|
||||||
pub enum ResponseSlot {
|
pub enum ResponseSlot {
|
||||||
Expecting(MultiLocation),
|
Expecting(MultiLocation),
|
||||||
Received(Response),
|
Received(Response),
|
||||||
@@ -222,7 +219,11 @@ impl OnResponse for TestResponseHandler {
|
|||||||
_ => false,
|
_ => 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| {
|
QUERIES.with(|q| {
|
||||||
q.borrow_mut().entry(query_id).and_modify(|v| {
|
q.borrow_mut().entry(query_id).and_modify(|v| {
|
||||||
if matches!(*v, ResponseSlot::Expecting(..)) {
|
if matches!(*v, ResponseSlot::Expecting(..)) {
|
||||||
@@ -254,7 +255,7 @@ parameter_types! {
|
|||||||
pub static AllowUnpaidFrom: Vec<MultiLocation> = vec![];
|
pub static AllowUnpaidFrom: Vec<MultiLocation> = vec![];
|
||||||
pub static AllowPaidFrom: Vec<MultiLocation> = vec![];
|
pub static AllowPaidFrom: Vec<MultiLocation> = vec![];
|
||||||
// 1_000_000_000_000 => 1 unit of asset for 1 unit of Weight.
|
// 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 = (
|
pub type TestBarrier = (
|
||||||
@@ -275,6 +276,6 @@ impl Config for TestConfig {
|
|||||||
type LocationInverter = LocationInverter<TestAncestry>;
|
type LocationInverter = LocationInverter<TestAncestry>;
|
||||||
type Barrier = TestBarrier;
|
type Barrier = TestBarrier;
|
||||||
type Weigher = FixedWeightBounds<UnitWeightCost, TestCall>;
|
type Weigher = FixedWeightBounds<UnitWeightCost, TestCall>;
|
||||||
type Trader = FixedRateOfConcreteFungible<WeightPrice, ()>;
|
type Trader = FixedRateOfFungible<WeightPrice, ()>;
|
||||||
type ResponseHandler = TestResponseHandler;
|
type ResponseHandler = TestResponseHandler;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ use frame_support::traits::{EnsureOrigin, Get, GetBacking, OriginTrait};
|
|||||||
use frame_system::RawOrigin as SystemRawOrigin;
|
use frame_system::RawOrigin as SystemRawOrigin;
|
||||||
use polkadot_parachain::primitives::IsSystem;
|
use polkadot_parachain::primitives::IsSystem;
|
||||||
use sp_std::{convert::TryInto, marker::PhantomData};
|
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};
|
use xcm_executor::traits::{Convert, ConvertOrigin};
|
||||||
|
|
||||||
/// Sovereign accounts use the system's `Signed` origin with an account ID derived from the `LocationConverter`.
|
/// 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
|
// We institute a root fallback so root can always represent the context. This
|
||||||
// guarantees that `successful_origin` will work.
|
// guarantees that `successful_origin` will work.
|
||||||
if o.caller() == Origin::root().caller() {
|
if o.caller() == Origin::root().caller() {
|
||||||
Ok(MultiLocation::Null)
|
Ok(MultiLocation::Here)
|
||||||
} else {
|
} else {
|
||||||
Err(o)
|
Err(o)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,16 +15,14 @@
|
|||||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use super::{mock::*, *};
|
use super::{mock::*, *};
|
||||||
use xcm::v0::{ExecuteXcm, NetworkId::Any, Order, Outcome, Response};
|
use xcm::latest::prelude::*;
|
||||||
use xcm_executor::{traits::*, Config, XcmExecutor};
|
use xcm_executor::{traits::*, Config, XcmExecutor};
|
||||||
use MultiAsset::*;
|
|
||||||
use Option::None;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_setup_works() {
|
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(
|
assert!(<TestConfig as Config>::IsReserve::filter_asset_location(
|
||||||
&ConcreteFungible { id: X1(Parent), amount: 100 },
|
&(X1(Parent), 100).into(),
|
||||||
&X1(Parent),
|
&X1(Parent),
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -34,22 +32,23 @@ fn basic_setup_works() {
|
|||||||
assert_eq!(to_account(X2(Parent, Parachain(50))), Ok(2050));
|
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: 1, network: Any })), Ok(1));
|
||||||
assert_eq!(to_account(X1(AccountIndex64 { index: 42, network: Any })), Ok(42));
|
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]
|
#[test]
|
||||||
fn weigher_should_work() {
|
fn weigher_should_work() {
|
||||||
let mut message = opaque::Xcm::ReserveAssetDeposit {
|
let mut message = opaque::Xcm::ReserveAssetDeposited {
|
||||||
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
|
assets: (X1(Parent), 100).into(),
|
||||||
effects: vec![
|
effects: vec![
|
||||||
Order::BuyExecution {
|
Order::BuyExecution {
|
||||||
fees: All,
|
fees: (X1(Parent), 1).into(),
|
||||||
weight: 0,
|
weight: 0,
|
||||||
debt: 30,
|
debt: 30,
|
||||||
halt_on_error: true,
|
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();
|
.into();
|
||||||
@@ -58,10 +57,8 @@ fn weigher_should_work() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn take_weight_credit_barrier_should_work() {
|
fn take_weight_credit_barrier_should_work() {
|
||||||
let mut message = opaque::Xcm::TransferAsset {
|
let mut message =
|
||||||
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
|
opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), beneficiary: Here };
|
||||||
dest: Null,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut weight_credit = 10;
|
let mut weight_credit = 10;
|
||||||
let r =
|
let r =
|
||||||
@@ -77,10 +74,8 @@ fn take_weight_credit_barrier_should_work() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn allow_unpaid_should_work() {
|
fn allow_unpaid_should_work() {
|
||||||
let mut message = opaque::Xcm::TransferAsset {
|
let mut message =
|
||||||
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
|
opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), beneficiary: Here };
|
||||||
dest: Null,
|
|
||||||
};
|
|
||||||
|
|
||||||
AllowUnpaidFrom::set(vec![X1(Parent)]);
|
AllowUnpaidFrom::set(vec![X1(Parent)]);
|
||||||
|
|
||||||
@@ -107,10 +102,8 @@ fn allow_unpaid_should_work() {
|
|||||||
fn allow_paid_should_work() {
|
fn allow_paid_should_work() {
|
||||||
AllowPaidFrom::set(vec![X1(Parent)]);
|
AllowPaidFrom::set(vec![X1(Parent)]);
|
||||||
|
|
||||||
let mut message = opaque::Xcm::TransferAsset {
|
let mut message =
|
||||||
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
|
opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), beneficiary: Here };
|
||||||
dest: Null,
|
|
||||||
};
|
|
||||||
|
|
||||||
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
let r = AllowTopLevelPaidExecutionFrom::<IsInVec<AllowPaidFrom>>::should_execute(
|
||||||
&X1(Parachain(1)),
|
&X1(Parachain(1)),
|
||||||
@@ -121,17 +114,19 @@ fn allow_paid_should_work() {
|
|||||||
);
|
);
|
||||||
assert_eq!(r, Err(()));
|
assert_eq!(r, Err(()));
|
||||||
|
|
||||||
let mut underpaying_message = opaque::Xcm::ReserveAssetDeposit {
|
let fees = (X1(Parent), 1).into();
|
||||||
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
|
let mut underpaying_message = opaque::Xcm::ReserveAssetDeposited {
|
||||||
|
assets: (X1(Parent), 100).into(),
|
||||||
effects: vec![
|
effects: vec![
|
||||||
Order::BuyExecution {
|
Order::BuyExecution {
|
||||||
fees: All,
|
fees,
|
||||||
weight: 0,
|
weight: 0,
|
||||||
debt: 20,
|
debt: 20,
|
||||||
halt_on_error: true,
|
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(()));
|
assert_eq!(r, Err(()));
|
||||||
|
|
||||||
let mut paying_message = opaque::Xcm::ReserveAssetDeposit {
|
let fees = (X1(Parent), 1).into();
|
||||||
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
|
let mut paying_message = opaque::Xcm::ReserveAssetDeposited {
|
||||||
|
assets: (X1(Parent), 100).into(),
|
||||||
effects: vec![
|
effects: vec![
|
||||||
Order::BuyExecution {
|
Order::BuyExecution {
|
||||||
fees: All,
|
fees,
|
||||||
weight: 0,
|
weight: 0,
|
||||||
debt: 30,
|
debt: 30,
|
||||||
halt_on_error: true,
|
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]
|
#[test]
|
||||||
fn paying_reserve_deposit_should_work() {
|
fn paying_reserve_deposit_should_work() {
|
||||||
AllowPaidFrom::set(vec![X1(Parent)]);
|
AllowPaidFrom::set(vec![X1(Parent)]);
|
||||||
add_reserve(X1(Parent), AllConcreteFungible { id: X1(Parent) });
|
add_reserve(X1(Parent), (Parent, WildFungible).into());
|
||||||
WeightPrice::set((X1(Parent), 1_000_000_000_000));
|
WeightPrice::set((Parent.into(), 1_000_000_000_000));
|
||||||
|
|
||||||
let origin = X1(Parent);
|
let origin = X1(Parent);
|
||||||
let message = Xcm::<TestCall>::ReserveAssetDeposit {
|
let fees = (X1(Parent), 30).into();
|
||||||
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
|
let message = Xcm::<TestCall>::ReserveAssetDeposited {
|
||||||
|
assets: (X1(Parent), 100).into(),
|
||||||
effects: vec![
|
effects: vec![
|
||||||
Order::<TestCall>::BuyExecution {
|
Order::<TestCall>::BuyExecution {
|
||||||
fees: All,
|
fees,
|
||||||
weight: 0,
|
weight: 0,
|
||||||
debt: 30,
|
debt: 30,
|
||||||
halt_on_error: true,
|
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 weight_limit = 50;
|
||||||
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||||
assert_eq!(r, Outcome::Complete(30));
|
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]
|
#[test]
|
||||||
@@ -208,19 +211,19 @@ fn transfer_should_work() {
|
|||||||
// we'll let them have message execution for free.
|
// we'll let them have message execution for free.
|
||||||
AllowUnpaidFrom::set(vec![X1(Parachain(1))]);
|
AllowUnpaidFrom::set(vec![X1(Parachain(1))]);
|
||||||
// Child parachain #1 owns 1000 tokens held by us in reserve.
|
// 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
|
// They want to transfer 100 of them to their sibling parachain #2
|
||||||
let r = XcmExecutor::<TestConfig>::execute_xcm(
|
let r = XcmExecutor::<TestConfig>::execute_xcm(
|
||||||
X1(Parachain(1)),
|
X1(Parachain(1)),
|
||||||
Xcm::TransferAsset {
|
Xcm::TransferAsset {
|
||||||
assets: vec![ConcreteFungible { id: Null, amount: 100 }],
|
assets: (Here, 100).into(),
|
||||||
dest: X1(AccountIndex64 { index: 3, network: Any }),
|
beneficiary: X1(AccountIndex64 { index: 3, network: Any }),
|
||||||
},
|
},
|
||||||
50,
|
50,
|
||||||
);
|
);
|
||||||
assert_eq!(r, Outcome::Complete(10));
|
assert_eq!(r, Outcome::Complete(10));
|
||||||
assert_eq!(assets(3), vec![ConcreteFungible { id: Null, amount: 100 }]);
|
assert_eq!(assets(3), vec![(Here, 100).into()]);
|
||||||
assert_eq!(assets(1001), vec![ConcreteFungible { id: Null, amount: 900 }]);
|
assert_eq!(assets(1001), vec![(Here, 900).into()]);
|
||||||
assert_eq!(sent_xcm(), vec![]);
|
assert_eq!(sent_xcm(), vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +231,7 @@ fn transfer_should_work() {
|
|||||||
fn reserve_transfer_should_work() {
|
fn reserve_transfer_should_work() {
|
||||||
AllowUnpaidFrom::set(vec![X1(Parachain(1))]);
|
AllowUnpaidFrom::set(vec![X1(Parachain(1))]);
|
||||||
// Child parachain #1 owns 1000 tokens held by us in reserve.
|
// 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.
|
// The remote account owned by gav.
|
||||||
let three = X1(AccountIndex64 { index: 3, network: Any });
|
let three = X1(AccountIndex64 { index: 3, network: Any });
|
||||||
|
|
||||||
@@ -237,22 +240,30 @@ fn reserve_transfer_should_work() {
|
|||||||
let r = XcmExecutor::<TestConfig>::execute_xcm(
|
let r = XcmExecutor::<TestConfig>::execute_xcm(
|
||||||
X1(Parachain(1)),
|
X1(Parachain(1)),
|
||||||
Xcm::TransferReserveAsset {
|
Xcm::TransferReserveAsset {
|
||||||
assets: vec![ConcreteFungible { id: Null, amount: 100 }],
|
assets: (Here, 100).into(),
|
||||||
dest: X1(Parachain(2)),
|
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,
|
50,
|
||||||
);
|
);
|
||||||
assert_eq!(r, Outcome::Complete(10));
|
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!(
|
assert_eq!(
|
||||||
sent_xcm(),
|
sent_xcm(),
|
||||||
vec![(
|
vec![(
|
||||||
X1(Parachain(2)),
|
X1(Parachain(2)),
|
||||||
Xcm::ReserveAssetDeposit {
|
Xcm::ReserveAssetDeposited {
|
||||||
assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }],
|
assets: (X1(Parent), 100).into(),
|
||||||
effects: vec![Order::DepositAsset { assets: vec![All], dest: three }],
|
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() {
|
fn paid_transacting_should_refund_payment_for_unused_weight() {
|
||||||
let one = X1(AccountIndex64 { index: 1, network: Any });
|
let one = X1(AccountIndex64 { index: 1, network: Any });
|
||||||
AllowPaidFrom::set(vec![one.clone()]);
|
AllowPaidFrom::set(vec![one.clone()]);
|
||||||
add_asset(1, ConcreteFungible { id: X1(Parent), amount: 100 });
|
add_asset(1, (Parent, 100).into());
|
||||||
WeightPrice::set((X1(Parent), 1_000_000_000_000));
|
WeightPrice::set((Parent.into(), 1_000_000_000_000));
|
||||||
|
|
||||||
let origin = one.clone();
|
let origin = one.clone();
|
||||||
|
let fees = (X1(Parent), 100).into();
|
||||||
let message = Xcm::<TestCall>::WithdrawAsset {
|
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![
|
effects: vec![
|
||||||
Order::<TestCall>::BuyExecution {
|
Order::<TestCall>::BuyExecution {
|
||||||
fees: All,
|
fees,
|
||||||
weight: 70,
|
weight: 70,
|
||||||
debt: 30,
|
debt: 30,
|
||||||
halt_on_error: true,
|
halt_on_error: true,
|
||||||
xcm: vec![Xcm::<TestCall>::Transact {
|
orders: vec![],
|
||||||
|
instructions: vec![Xcm::<TestCall>::Transact {
|
||||||
origin_type: OriginKind::Native,
|
origin_type: OriginKind::Native,
|
||||||
require_weight_at_most: 60,
|
require_weight_at_most: 60,
|
||||||
// call estimated at 70 but only takes 10.
|
// call estimated at 70 but only takes 10.
|
||||||
call: TestCall::Any(60, Some(10)).encode().into(),
|
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 weight_limit = 100;
|
||||||
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
let r = XcmExecutor::<TestConfig>::execute_xcm(origin, message, weight_limit);
|
||||||
assert_eq!(r, Outcome::Complete(50));
|
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]
|
#[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.
|
// 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());
|
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 message = Xcm::<TestCall>::QueryResponse { query_id, response: the_response.clone() };
|
||||||
let weight_limit = 10;
|
let weight_limit = 10;
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use frame_support::{
|
|||||||
use parity_scale_codec::Decode;
|
use parity_scale_codec::Decode;
|
||||||
use sp_runtime::traits::{SaturatedConversion, Saturating, Zero};
|
use sp_runtime::traits::{SaturatedConversion, Saturating, Zero};
|
||||||
use sp_std::{convert::TryInto, marker::PhantomData, result::Result};
|
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::{
|
use xcm_executor::{
|
||||||
traits::{WeightBounds, WeightTrader},
|
traits::{WeightBounds, WeightTrader},
|
||||||
Assets,
|
Assets,
|
||||||
@@ -36,23 +36,13 @@ impl<T: Get<Weight>, C: Decode + GetDispatchInfo> WeightBounds<C> for FixedWeigh
|
|||||||
Xcm::RelayedFrom { ref mut message, .. } =>
|
Xcm::RelayedFrom { ref mut message, .. } =>
|
||||||
T::get().saturating_add(Self::shallow(message.as_mut())?),
|
T::get().saturating_add(Self::shallow(message.as_mut())?),
|
||||||
Xcm::WithdrawAsset { effects, .. } |
|
Xcm::WithdrawAsset { effects, .. } |
|
||||||
Xcm::ReserveAssetDeposit { effects, .. } |
|
Xcm::ReserveAssetDeposited { effects, .. } |
|
||||||
Xcm::TeleportAsset { effects, .. } => {
|
Xcm::ReceiveTeleportedAsset { effects, .. } => {
|
||||||
let inner: Weight = effects
|
let mut extra = T::get();
|
||||||
.iter_mut()
|
for order in effects.iter_mut() {
|
||||||
.map(|effect| match effect {
|
extra.saturating_accrue(Self::shallow_order(order)?);
|
||||||
Order::BuyExecution { .. } => {
|
}
|
||||||
// On success, execution of this will result in more weight being consumed but
|
extra
|
||||||
// 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)
|
|
||||||
},
|
},
|
||||||
_ => T::get(),
|
_ => T::get(),
|
||||||
})
|
})
|
||||||
@@ -61,19 +51,46 @@ impl<T: Get<Weight>, C: Decode + GetDispatchInfo> WeightBounds<C> for FixedWeigh
|
|||||||
Ok(match message {
|
Ok(match message {
|
||||||
Xcm::RelayedFrom { ref mut message, .. } => Self::deep(message.as_mut())?,
|
Xcm::RelayedFrom { ref mut message, .. } => Self::deep(message.as_mut())?,
|
||||||
Xcm::WithdrawAsset { effects, .. } |
|
Xcm::WithdrawAsset { effects, .. } |
|
||||||
Xcm::ReserveAssetDeposit { effects, .. } |
|
Xcm::ReserveAssetDeposited { effects, .. } |
|
||||||
Xcm::TeleportAsset { effects, .. } => {
|
Xcm::ReceiveTeleportedAsset { effects, .. } => {
|
||||||
let mut extra = 0;
|
let mut extra = 0;
|
||||||
for effect in effects.iter_mut() {
|
for order in effects.iter_mut() {
|
||||||
match effect {
|
extra.saturating_accrue(Self::deep_order(order)?);
|
||||||
Order::BuyExecution { xcm, .. } =>
|
}
|
||||||
for message in xcm.iter_mut() {
|
extra
|
||||||
extra.saturating_accrue(
|
},
|
||||||
Self::shallow(message)?.saturating_add(Self::deep(message)?),
|
_ => 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
|
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
|
/// The constant `Get` type parameter should be the concrete fungible ID and the amount of it required for
|
||||||
/// one second of weight.
|
/// one second of weight.
|
||||||
|
#[deprecated = "Use `FixedRateOfFungible` instead"]
|
||||||
pub struct FixedRateOfConcreteFungible<T: Get<(MultiLocation, u128)>, R: TakeRevenue>(
|
pub struct FixedRateOfConcreteFungible<T: Get<(MultiLocation, u128)>, R: TakeRevenue>(
|
||||||
Weight,
|
Weight,
|
||||||
u128,
|
u128,
|
||||||
PhantomData<(T, R)>,
|
PhantomData<(T, R)>,
|
||||||
);
|
);
|
||||||
|
#[allow(deprecated)]
|
||||||
impl<T: Get<(MultiLocation, u128)>, R: TakeRevenue> WeightTrader
|
impl<T: Get<(MultiLocation, u128)>, R: TakeRevenue> WeightTrader
|
||||||
for FixedRateOfConcreteFungible<T, R>
|
for FixedRateOfConcreteFungible<T, R>
|
||||||
{
|
{
|
||||||
@@ -114,28 +133,80 @@ impl<T: Get<(MultiLocation, u128)>, R: TakeRevenue> WeightTrader
|
|||||||
let (id, units_per_second) = T::get();
|
let (id, units_per_second) = T::get();
|
||||||
use frame_support::weights::constants::WEIGHT_PER_SECOND;
|
use frame_support::weights::constants::WEIGHT_PER_SECOND;
|
||||||
let amount = units_per_second * (weight as u128) / (WEIGHT_PER_SECOND as u128);
|
let amount = units_per_second * (weight as u128) / (WEIGHT_PER_SECOND as u128);
|
||||||
let required = MultiAsset::ConcreteFungible { amount, id };
|
let unused = payment.checked_sub((id, amount).into()).map_err(|_| Error::TooExpensive)?;
|
||||||
let (unused, _) = payment.less(required).map_err(|_| Error::TooExpensive)?;
|
|
||||||
self.0 = self.0.saturating_add(weight);
|
self.0 = self.0.saturating_add(weight);
|
||||||
self.1 = self.1.saturating_add(amount);
|
self.1 = self.1.saturating_add(amount);
|
||||||
Ok(unused)
|
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 (id, units_per_second) = T::get();
|
||||||
let weight = weight.min(self.0);
|
let weight = weight.min(self.0);
|
||||||
let amount = units_per_second * (weight as u128) / 1_000_000_000_000u128;
|
let amount = units_per_second * (weight as u128) / 1_000_000_000_000u128;
|
||||||
self.0 -= weight;
|
self.0 -= weight;
|
||||||
self.1 = self.1.saturating_sub(amount);
|
self.1 = self.1.saturating_sub(amount);
|
||||||
let result = MultiAsset::ConcreteFungible { amount, id };
|
if amount > 0 {
|
||||||
result
|
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) {
|
fn drop(&mut self) {
|
||||||
let revenue = MultiAsset::ConcreteFungible { amount: self.1, id: T::get().0 };
|
if self.1 > 0 {
|
||||||
R::take_revenue(revenue);
|
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> {
|
fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, Error> {
|
||||||
let amount = WeightToFee::calc(&weight);
|
let amount = WeightToFee::calc(&weight);
|
||||||
let required = MultiAsset::ConcreteFungible {
|
let u128_amount: u128 = amount.try_into().map_err(|_| Error::Overflow)?;
|
||||||
amount: amount.try_into().map_err(|_| Error::Overflow)?,
|
let required = (Concrete(AssetId::get()), u128_amount).into();
|
||||||
id: AssetId::get(),
|
let unused = payment.checked_sub(required).map_err(|_| Error::TooExpensive)?;
|
||||||
};
|
|
||||||
let (unused, _) = payment.less(required).map_err(|_| Error::TooExpensive)?;
|
|
||||||
self.0 = self.0.saturating_add(weight);
|
self.0 = self.0.saturating_add(weight);
|
||||||
self.1 = self.1.saturating_add(amount);
|
self.1 = self.1.saturating_add(amount);
|
||||||
Ok(unused)
|
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 weight = weight.min(self.0);
|
||||||
let amount = WeightToFee::calc(&weight);
|
let amount = WeightToFee::calc(&weight);
|
||||||
self.0 -= weight;
|
self.0 -= weight;
|
||||||
self.1 = self.1.saturating_sub(amount);
|
self.1 = self.1.saturating_sub(amount);
|
||||||
let result =
|
let amount: u128 = amount.saturated_into();
|
||||||
MultiAsset::ConcreteFungible { amount: amount.saturated_into(), id: AssetId::get() };
|
if amount > 0 {
|
||||||
result
|
Some((AssetId::get(), amount).into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<
|
impl<
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,7 @@ use frame_support::{
|
|||||||
dispatch::{Dispatchable, Parameter},
|
dispatch::{Dispatchable, Parameter},
|
||||||
weights::{GetDispatchInfo, PostDispatchInfo},
|
weights::{GetDispatchInfo, PostDispatchInfo},
|
||||||
};
|
};
|
||||||
use xcm::v0::SendXcm;
|
use xcm::latest::SendXcm;
|
||||||
|
|
||||||
/// The trait to parameterize the `XcmExecutor`.
|
/// The trait to parameterize the `XcmExecutor`.
|
||||||
pub trait Config {
|
pub trait Config {
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ use frame_support::{
|
|||||||
weights::GetDispatchInfo,
|
weights::GetDispatchInfo,
|
||||||
};
|
};
|
||||||
use sp_std::{marker::PhantomData, prelude::*};
|
use sp_std::{marker::PhantomData, prelude::*};
|
||||||
use xcm::v0::{
|
use xcm::latest::{
|
||||||
Error as XcmError, ExecuteXcm, MultiAsset, MultiLocation, Order, Outcome, Response, SendXcm,
|
Error as XcmError, ExecuteXcm, MultiAssets, MultiLocation, Order, Outcome, Response, SendXcm,
|
||||||
Xcm,
|
Xcm,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ use traits::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
mod assets;
|
mod assets;
|
||||||
pub use assets::{AssetId, Assets};
|
pub use assets::Assets;
|
||||||
mod config;
|
mod config;
|
||||||
pub use config::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> {
|
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);
|
let inv_dest = Config::LocationInverter::invert_location(&dest);
|
||||||
assets.prepend_location(&inv_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.
|
/// 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 }) => {
|
(origin, Xcm::WithdrawAsset { assets, effects }) => {
|
||||||
// Take `assets` from the origin account (on-chain) and place in holding.
|
// Take `assets` from the origin account (on-chain) and place in holding.
|
||||||
let mut holding = Assets::default();
|
let mut holding = Assets::default();
|
||||||
for asset in assets {
|
for asset in assets.inner() {
|
||||||
ensure!(!asset.is_wildcard(), XcmError::Wildcard);
|
|
||||||
let withdrawn = Config::AssetTransactor::withdraw_asset(&asset, &origin)?;
|
let withdrawn = Config::AssetTransactor::withdraw_asset(&asset, &origin)?;
|
||||||
holding.saturating_subsume_all(withdrawn);
|
holding.subsume_assets(withdrawn);
|
||||||
}
|
}
|
||||||
Some((holding, effects))
|
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.
|
// check whether we trust origin to be our reserve location for this asset.
|
||||||
for asset in assets.iter() {
|
for asset in assets.inner() {
|
||||||
ensure!(!asset.is_wildcard(), XcmError::Wildcard);
|
|
||||||
// We only trust the origin to send us assets that they identify as their
|
// We only trust the origin to send us assets that they identify as their
|
||||||
// sovereign assets.
|
// sovereign assets.
|
||||||
ensure!(
|
ensure!(
|
||||||
@@ -162,31 +160,28 @@ impl<Config: config::Config> XcmExecutor<Config> {
|
|||||||
XcmError::UntrustedReserveLocation
|
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.
|
// Take `assets` from the origin account (on-chain) and place into dest account.
|
||||||
for asset in assets {
|
for asset in assets.inner() {
|
||||||
ensure!(!asset.is_wildcard(), XcmError::Wildcard);
|
Config::AssetTransactor::beam_asset(&asset, &origin, &beneficiary)?;
|
||||||
Config::AssetTransactor::teleport_asset(&asset, &origin, &dest)?;
|
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
(origin, Xcm::TransferReserveAsset { mut assets, dest, effects }) => {
|
(origin, Xcm::TransferReserveAsset { mut assets, dest, effects }) => {
|
||||||
// Take `assets` from the origin account (on-chain) and place into dest account.
|
// Take `assets` from the origin account (on-chain) and place into dest account.
|
||||||
let inv_dest = Config::LocationInverter::invert_location(&dest);
|
let inv_dest = Config::LocationInverter::invert_location(&dest);
|
||||||
for asset in assets.iter_mut() {
|
for asset in assets.inner() {
|
||||||
ensure!(!asset.is_wildcard(), XcmError::Wildcard);
|
Config::AssetTransactor::beam_asset(asset, &origin, &dest)?;
|
||||||
Config::AssetTransactor::teleport_asset(&asset, &origin, &dest)?;
|
|
||||||
asset.reanchor(&inv_dest)?;
|
|
||||||
}
|
}
|
||||||
Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposit { assets, effects })?;
|
assets.reanchor(&inv_dest)?;
|
||||||
|
Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposited { assets, effects })?;
|
||||||
None
|
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.
|
// check whether we trust origin to teleport this asset to us via config trait.
|
||||||
for asset in assets.iter() {
|
for asset in assets.inner() {
|
||||||
ensure!(!asset.is_wildcard(), XcmError::Wildcard);
|
|
||||||
// We only trust the origin to send us assets that they identify as their
|
// We only trust the origin to send us assets that they identify as their
|
||||||
// sovereign assets.
|
// sovereign assets.
|
||||||
ensure!(
|
ensure!(
|
||||||
@@ -198,7 +193,7 @@ impl<Config: config::Config> XcmExecutor<Config> {
|
|||||||
// don't want to punish a possibly innocent chain/user).
|
// don't want to punish a possibly innocent chain/user).
|
||||||
Config::AssetTransactor::can_check_in(&origin, asset)?;
|
Config::AssetTransactor::can_check_in(&origin, asset)?;
|
||||||
}
|
}
|
||||||
for asset in assets.iter() {
|
for asset in assets.inner() {
|
||||||
Config::AssetTransactor::check_in(&origin, asset);
|
Config::AssetTransactor::check_in(&origin, asset);
|
||||||
}
|
}
|
||||||
Some((Assets::from(assets), effects))
|
Some((Assets::from(assets), effects))
|
||||||
@@ -252,41 +247,41 @@ impl<Config: config::Config> XcmExecutor<Config> {
|
|||||||
|
|
||||||
if let Some((mut holding, effects)) = maybe_holding_effects {
|
if let Some((mut holding, effects)) = maybe_holding_effects {
|
||||||
for effect in effects.into_iter() {
|
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)
|
Ok(total_surplus)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_effects(
|
fn execute_orders(
|
||||||
origin: &MultiLocation,
|
origin: &MultiLocation,
|
||||||
holding: &mut Assets,
|
holding: &mut Assets,
|
||||||
effect: Order<Config::Call>,
|
order: Order<Config::Call>,
|
||||||
trader: &mut Config::Trader,
|
trader: &mut Config::Trader,
|
||||||
) -> Result<Weight, XcmError> {
|
) -> Result<Weight, XcmError> {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
target: "xcm::execute_effects",
|
target: "xcm::execute_orders",
|
||||||
"origin: {:?}, holding: {:?}, effect: {:?}",
|
"origin: {:?}, holding: {:?}, effect: {:?}",
|
||||||
origin,
|
origin,
|
||||||
holding,
|
holding,
|
||||||
effect,
|
order,
|
||||||
);
|
);
|
||||||
let mut total_surplus = 0;
|
let mut total_surplus = 0;
|
||||||
match effect {
|
match order {
|
||||||
Order::DepositAsset { assets, dest } => {
|
Order::DepositAsset { assets, max_assets, beneficiary } => {
|
||||||
let deposited = holding.saturating_take(assets);
|
let deposited = holding.limited_saturating_take(assets, max_assets as usize);
|
||||||
for asset in deposited.into_assets_iter() {
|
for asset in deposited.into_assets_iter() {
|
||||||
Config::AssetTransactor::deposit_asset(&asset, &dest)?;
|
Config::AssetTransactor::deposit_asset(&asset, &beneficiary)?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Order::DepositReserveAsset { assets, dest, effects } => {
|
Order::DepositReserveAsset { assets, max_assets, dest, effects } => {
|
||||||
let deposited = holding.saturating_take(assets);
|
let deposited = holding.limited_saturating_take(assets, max_assets as usize);
|
||||||
for asset in deposited.assets_iter() {
|
for asset in deposited.assets_iter() {
|
||||||
Config::AssetTransactor::deposit_asset(&asset, &dest)?;
|
Config::AssetTransactor::deposit_asset(&asset, &dest)?;
|
||||||
}
|
}
|
||||||
let assets = Self::reanchored(deposited, &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 } => {
|
Order::InitiateReserveWithdraw { assets, reserve, effects } => {
|
||||||
let assets = Self::reanchored(holding.saturating_take(assets), &reserve);
|
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);
|
Config::AssetTransactor::check_out(&origin, &asset);
|
||||||
}
|
}
|
||||||
let assets = Self::reanchored(assets, &dest);
|
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 } => {
|
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(
|
Config::XcmSender::send_xcm(
|
||||||
dest,
|
dest,
|
||||||
Xcm::QueryResponse { query_id, response: Response::Assets(assets) },
|
Xcm::QueryResponse { query_id, response: Response::Assets(assets) },
|
||||||
)?;
|
)?;
|
||||||
},
|
},
|
||||||
Order::BuyExecution { fees, weight, debt, halt_on_error, xcm } => {
|
Order::BuyExecution { fees, weight, debt, halt_on_error, orders, instructions } => {
|
||||||
// pay for `weight` using up to `fees` of the holding account.
|
// pay for `weight` using up to `fees` of the holding register.
|
||||||
let purchasing_weight =
|
let purchasing_weight =
|
||||||
Weight::from(weight.checked_add(debt).ok_or(XcmError::Overflow)?);
|
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)?;
|
let unspent = trader.buy_weight(purchasing_weight, max_fee)?;
|
||||||
holding.saturating_subsume_all(unspent);
|
holding.subsume_assets(unspent);
|
||||||
|
|
||||||
let mut remaining_weight = weight;
|
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(
|
match Self::do_execute_xcm(
|
||||||
origin.clone(),
|
origin.clone(),
|
||||||
false,
|
false,
|
||||||
message,
|
instruction,
|
||||||
&mut remaining_weight,
|
&mut remaining_weight,
|
||||||
None,
|
None,
|
||||||
trader,
|
trader,
|
||||||
@@ -331,7 +334,9 @@ impl<Config: config::Config> XcmExecutor<Config> {
|
|||||||
Ok(surplus) => total_surplus += surplus,
|
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)?,
|
_ => return Err(XcmError::UnhandledEffect)?,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
use parity_scale_codec::{Decode, Encode};
|
use parity_scale_codec::{Decode, Encode};
|
||||||
use sp_std::{borrow::Borrow, convert::TryFrom, prelude::*, result::Result};
|
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
|
/// 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.
|
/// 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.
|
/// which is passed to the next convert item.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use xcm::v0::{MultiLocation, Junction, OriginKind};
|
/// # use xcm::latest::{MultiLocation, Junction, OriginKind};
|
||||||
/// # use xcm_executor::traits::ConvertOrigin;
|
/// # use xcm_executor::traits::ConvertOrigin;
|
||||||
/// // A convertor that will bump the para id and pass it to the next one.
|
/// // A convertor that will bump the para id and pass it to the next one.
|
||||||
/// struct BumpParaId;
|
/// struct BumpParaId;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
// 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.
|
/// Filters assets/location pairs.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use xcm::v0::MultiAsset;
|
use xcm::latest::MultiAsset;
|
||||||
|
|
||||||
pub trait MatchesFungible<Balance> {
|
pub trait MatchesFungible<Balance> {
|
||||||
fn matches_fungible(a: &MultiAsset) -> Option<Balance>;
|
fn matches_fungible(a: &MultiAsset) -> Option<Balance>;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use sp_std::result;
|
use sp_std::result;
|
||||||
use xcm::v0::{Error as XcmError, MultiAsset};
|
use xcm::latest::{Error as XcmError, MultiAsset};
|
||||||
|
|
||||||
/// Errors associated with [`MatchesFungibles`] operation.
|
/// Errors associated with [`MatchesFungibles`] operation.
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use frame_support::weights::Weight;
|
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.
|
/// Define what needs to be done upon receiving a query response.
|
||||||
pub trait OnResponse {
|
pub trait OnResponse {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
use frame_support::weights::Weight;
|
use frame_support::weights::Weight;
|
||||||
use sp_std::result::Result;
|
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.
|
/// Trait to determine whether the execution engine should actually execute a given XCM.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
use crate::Assets;
|
use crate::Assets;
|
||||||
use sp_std::result::Result;
|
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.
|
/// Facility for asset transacting.
|
||||||
///
|
///
|
||||||
@@ -89,7 +89,7 @@ pub trait TransactAsset {
|
|||||||
/// Move an `asset` `from` one location in `to` another location.
|
/// 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.
|
/// 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,
|
asset: &MultiAsset,
|
||||||
from: &MultiLocation,
|
from: &MultiLocation,
|
||||||
to: &MultiLocation,
|
to: &MultiLocation,
|
||||||
@@ -111,7 +111,7 @@ impl TransactAsset for Tuple {
|
|||||||
fn can_check_in(origin: &MultiLocation, what: &MultiAsset) -> XcmResult {
|
fn can_check_in(origin: &MultiLocation, what: &MultiAsset) -> XcmResult {
|
||||||
for_tuples!( #(
|
for_tuples!( #(
|
||||||
match Tuple::can_check_in(origin, what) {
|
match Tuple::can_check_in(origin, what) {
|
||||||
Err(XcmError::AssetNotFound | XcmError::Unimplemented) => (),
|
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
|
||||||
r => return r,
|
r => return r,
|
||||||
}
|
}
|
||||||
)* );
|
)* );
|
||||||
@@ -139,7 +139,7 @@ impl TransactAsset for Tuple {
|
|||||||
fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> XcmResult {
|
fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> XcmResult {
|
||||||
for_tuples!( #(
|
for_tuples!( #(
|
||||||
match Tuple::deposit_asset(what, who) {
|
match Tuple::deposit_asset(what, who) {
|
||||||
Err(XcmError::AssetNotFound | XcmError::Unimplemented) => (),
|
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
|
||||||
r => return r,
|
r => return r,
|
||||||
}
|
}
|
||||||
)* );
|
)* );
|
||||||
@@ -155,7 +155,7 @@ impl TransactAsset for Tuple {
|
|||||||
fn withdraw_asset(what: &MultiAsset, who: &MultiLocation) -> Result<Assets, XcmError> {
|
fn withdraw_asset(what: &MultiAsset, who: &MultiLocation) -> Result<Assets, XcmError> {
|
||||||
for_tuples!( #(
|
for_tuples!( #(
|
||||||
match Tuple::withdraw_asset(what, who) {
|
match Tuple::withdraw_asset(what, who) {
|
||||||
Err(XcmError::AssetNotFound | XcmError::Unimplemented) => (),
|
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
|
||||||
r => return r,
|
r => return r,
|
||||||
}
|
}
|
||||||
)* );
|
)* );
|
||||||
@@ -175,7 +175,7 @@ impl TransactAsset for Tuple {
|
|||||||
) -> Result<Assets, XcmError> {
|
) -> Result<Assets, XcmError> {
|
||||||
for_tuples!( #(
|
for_tuples!( #(
|
||||||
match Tuple::transfer_asset(what, from, to) {
|
match Tuple::transfer_asset(what, from, to) {
|
||||||
Err(XcmError::AssetNotFound | XcmError::Unimplemented) => (),
|
Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (),
|
||||||
r => return r,
|
r => return r,
|
||||||
}
|
}
|
||||||
)* );
|
)* );
|
||||||
@@ -193,6 +193,7 @@ impl TransactAsset for Tuple {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use MultiLocation::Here;
|
||||||
|
|
||||||
pub struct UnimplementedTransactor;
|
pub struct UnimplementedTransactor;
|
||||||
impl TransactAsset for UnimplementedTransactor {}
|
impl TransactAsset for UnimplementedTransactor {}
|
||||||
@@ -272,7 +273,7 @@ mod tests {
|
|||||||
(UnimplementedTransactor, NotFoundTransactor, UnimplementedTransactor);
|
(UnimplementedTransactor, NotFoundTransactor, UnimplementedTransactor);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null),
|
MultiTransactor::deposit_asset(&(Here, 1).into(), &Here),
|
||||||
Err(XcmError::AssetNotFound)
|
Err(XcmError::AssetNotFound)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -281,7 +282,7 @@ mod tests {
|
|||||||
fn unimplemented_and_not_found_continue_iteration() {
|
fn unimplemented_and_not_found_continue_iteration() {
|
||||||
type MultiTransactor = (UnimplementedTransactor, NotFoundTransactor, SuccessfulTransactor);
|
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]
|
#[test]
|
||||||
@@ -289,7 +290,7 @@ mod tests {
|
|||||||
type MultiTransactor = (OverflowTransactor, SuccessfulTransactor);
|
type MultiTransactor = (OverflowTransactor, SuccessfulTransactor);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null),
|
MultiTransactor::deposit_asset(&(Here, 1).into(), &Here),
|
||||||
Err(XcmError::Overflow)
|
Err(XcmError::Overflow)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -298,6 +299,6 @@ mod tests {
|
|||||||
fn success_stops_iteration() {
|
fn success_stops_iteration() {
|
||||||
type MultiTransactor = (SuccessfulTransactor, OverflowTransactor);
|
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 crate::Assets;
|
||||||
use frame_support::weights::Weight;
|
use frame_support::weights::Weight;
|
||||||
use sp_std::result::Result;
|
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.
|
/// Determine the weight of an XCM message.
|
||||||
pub trait WeightBounds<Call> {
|
pub trait WeightBounds<Call> {
|
||||||
@@ -71,8 +71,8 @@ pub trait WeightTrader: Sized {
|
|||||||
/// purchased using `buy_weight`.
|
/// purchased using `buy_weight`.
|
||||||
///
|
///
|
||||||
/// Default implementation refunds nothing.
|
/// Default implementation refunds nothing.
|
||||||
fn refund_weight(&mut self, _weight: Weight) -> MultiAsset {
|
fn refund_weight(&mut self, _weight: Weight) -> Option<MultiAsset> {
|
||||||
MultiAsset::None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,9 @@
|
|||||||
mod parachain;
|
mod parachain;
|
||||||
mod relay_chain;
|
mod relay_chain;
|
||||||
|
|
||||||
use sp_runtime::AccountId32;
|
|
||||||
use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain};
|
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! {
|
decl_test_parachain! {
|
||||||
pub struct ParaA {
|
pub struct ParaA {
|
||||||
@@ -100,13 +99,7 @@ mod tests {
|
|||||||
|
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use frame_support::assert_ok;
|
use frame_support::assert_ok;
|
||||||
use xcm::v0::{
|
use xcm::latest::prelude::*;
|
||||||
Junction::{self, Parachain, Parent},
|
|
||||||
MultiAsset::*,
|
|
||||||
MultiLocation::*,
|
|
||||||
NetworkId, OriginKind,
|
|
||||||
Xcm::*,
|
|
||||||
};
|
|
||||||
use xcm_simulator::TestExt;
|
use xcm_simulator::TestExt;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -118,7 +111,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
Relay::execute_with(|| {
|
Relay::execute_with(|| {
|
||||||
assert_ok!(RelayChainPalletXcm::send_xcm(
|
assert_ok!(RelayChainPalletXcm::send_xcm(
|
||||||
Null,
|
Here,
|
||||||
X1(Parachain(1)),
|
X1(Parachain(1)),
|
||||||
Transact {
|
Transact {
|
||||||
origin_type: OriginKind::SovereignAccount,
|
origin_type: OriginKind::SovereignAccount,
|
||||||
@@ -145,7 +138,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
ParaA::execute_with(|| {
|
ParaA::execute_with(|| {
|
||||||
assert_ok!(ParachainPalletXcm::send_xcm(
|
assert_ok!(ParachainPalletXcm::send_xcm(
|
||||||
Null,
|
Here,
|
||||||
X1(Parent),
|
X1(Parent),
|
||||||
Transact {
|
Transact {
|
||||||
origin_type: OriginKind::SovereignAccount,
|
origin_type: OriginKind::SovereignAccount,
|
||||||
@@ -172,7 +165,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
ParaA::execute_with(|| {
|
ParaA::execute_with(|| {
|
||||||
assert_ok!(ParachainPalletXcm::send_xcm(
|
assert_ok!(ParachainPalletXcm::send_xcm(
|
||||||
Null,
|
Here,
|
||||||
X2(Parent, Parachain(2)),
|
X2(Parent, Parachain(2)),
|
||||||
Transact {
|
Transact {
|
||||||
origin_type: OriginKind::SovereignAccount,
|
origin_type: OriginKind::SovereignAccount,
|
||||||
@@ -198,9 +191,10 @@ mod tests {
|
|||||||
assert_ok!(RelayChainPalletXcm::reserve_transfer_assets(
|
assert_ok!(RelayChainPalletXcm::reserve_transfer_assets(
|
||||||
relay_chain::Origin::signed(ALICE),
|
relay_chain::Origin::signed(ALICE),
|
||||||
X1(Parachain(1)),
|
X1(Parachain(1)),
|
||||||
X1(Junction::AccountId32 { network: NetworkId::Any, id: ALICE.into() }),
|
X1(AccountId32 { network: Any, id: ALICE.into() }),
|
||||||
vec![ConcreteFungible { id: Null, amount: 123 }],
|
(Here, 123).into(),
|
||||||
123,
|
0,
|
||||||
|
3,
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -35,19 +35,10 @@ use polkadot_core_primitives::BlockNumber as RelayBlockNumber;
|
|||||||
use polkadot_parachain::primitives::{
|
use polkadot_parachain::primitives::{
|
||||||
DmpMessageHandler, Id as ParaId, Sibling, XcmpMessageFormat, XcmpMessageHandler,
|
DmpMessageHandler, Id as ParaId, Sibling, XcmpMessageFormat, XcmpMessageHandler,
|
||||||
};
|
};
|
||||||
use xcm::{
|
use xcm::{latest::prelude::*, VersionedXcm};
|
||||||
v0::{
|
|
||||||
Error as XcmError, ExecuteXcm,
|
|
||||||
Junction::{Parachain, Parent},
|
|
||||||
MultiAsset,
|
|
||||||
MultiLocation::{self, X1},
|
|
||||||
NetworkId, Outcome, Xcm,
|
|
||||||
},
|
|
||||||
VersionedXcm,
|
|
||||||
};
|
|
||||||
use xcm_builder::{
|
use xcm_builder::{
|
||||||
AccountId32Aliases, AllowUnpaidExecutionFrom, CurrencyAdapter as XcmCurrencyAdapter,
|
AccountId32Aliases, AllowUnpaidExecutionFrom, CurrencyAdapter as XcmCurrencyAdapter,
|
||||||
EnsureXcmOrigin, FixedRateOfConcreteFungible, FixedWeightBounds, IsConcrete, LocationInverter,
|
EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, IsConcrete, LocationInverter,
|
||||||
NativeAsset, ParentIsDefault, SiblingParachainConvertsVia, SignedAccountId32AsNative,
|
NativeAsset, ParentIsDefault, SiblingParachainConvertsVia, SignedAccountId32AsNative,
|
||||||
SignedToAccountId32, SovereignSignedViaLocation,
|
SignedToAccountId32, SovereignSignedViaLocation,
|
||||||
};
|
};
|
||||||
@@ -129,7 +120,7 @@ pub type XcmOriginToCallOrigin = (
|
|||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const UnitWeightCost: Weight = 1;
|
pub const UnitWeightCost: Weight = 1;
|
||||||
pub KsmPerSecond: (MultiLocation, u128) = (X1(Parent), 1);
|
pub KsmPerSecond: (AssetId, u128) = (Concrete(X1(Parent)), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type LocalAssetTransactor =
|
pub type LocalAssetTransactor =
|
||||||
@@ -149,7 +140,7 @@ impl Config for XcmConfig {
|
|||||||
type LocationInverter = LocationInverter<Ancestry>;
|
type LocationInverter = LocationInverter<Ancestry>;
|
||||||
type Barrier = Barrier;
|
type Barrier = Barrier;
|
||||||
type Weigher = FixedWeightBounds<UnitWeightCost, Call>;
|
type Weigher = FixedWeightBounds<UnitWeightCost, Call>;
|
||||||
type Trader = FixedRateOfConcreteFungible<KsmPerSecond, ()>;
|
type Trader = FixedRateOfFungible<KsmPerSecond, ()>;
|
||||||
type ResponseHandler = ();
|
type ResponseHandler = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,13 +26,12 @@ use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32};
|
|||||||
|
|
||||||
use polkadot_parachain::primitives::Id as ParaId;
|
use polkadot_parachain::primitives::Id as ParaId;
|
||||||
use polkadot_runtime_parachains::{configuration, origin, shared, ump};
|
use polkadot_runtime_parachains::{configuration, origin, shared, ump};
|
||||||
use xcm::v0::{MultiAsset, MultiLocation, NetworkId};
|
use xcm::latest::prelude::*;
|
||||||
use xcm_builder::{
|
use xcm_builder::{
|
||||||
AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative,
|
AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative,
|
||||||
ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
|
ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
|
||||||
CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfConcreteFungible, FixedWeightBounds,
|
CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete,
|
||||||
IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32,
|
LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation,
|
||||||
SovereignSignedViaLocation,
|
|
||||||
};
|
};
|
||||||
use xcm_executor::{Config, XcmExecutor};
|
use xcm_executor::{Config, XcmExecutor};
|
||||||
|
|
||||||
@@ -92,10 +91,10 @@ impl shared::Config for Runtime {}
|
|||||||
impl configuration::Config for Runtime {}
|
impl configuration::Config for Runtime {}
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const KsmLocation: MultiLocation = MultiLocation::Null;
|
pub const KsmLocation: MultiLocation = MultiLocation::Here;
|
||||||
pub const KusamaNetwork: NetworkId = NetworkId::Kusama;
|
pub const KusamaNetwork: NetworkId = NetworkId::Kusama;
|
||||||
pub const AnyNetwork: NetworkId = NetworkId::Any;
|
pub const AnyNetwork: NetworkId = NetworkId::Any;
|
||||||
pub Ancestry: MultiLocation = MultiLocation::Null;
|
pub Ancestry: MultiLocation = MultiLocation::Here;
|
||||||
pub UnitWeightCost: Weight = 1_000;
|
pub UnitWeightCost: Weight = 1_000;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +113,7 @@ type LocalOriginConverter = (
|
|||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const BaseXcmWeight: Weight = 1_000;
|
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;
|
pub type XcmRouter = super::RelayChainXcmRouter;
|
||||||
@@ -131,7 +130,7 @@ impl Config for XcmConfig {
|
|||||||
type LocationInverter = LocationInverter<Ancestry>;
|
type LocationInverter = LocationInverter<Ancestry>;
|
||||||
type Barrier = Barrier;
|
type Barrier = Barrier;
|
||||||
type Weigher = FixedWeightBounds<BaseXcmWeight, Call>;
|
type Weigher = FixedWeightBounds<BaseXcmWeight, Call>;
|
||||||
type Trader = FixedRateOfConcreteFungible<KsmPerSecond, ()>;
|
type Trader = FixedRateOfFungible<KsmPerSecond, ()>;
|
||||||
type ResponseHandler = ();
|
type ResponseHandler = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ pub use polkadot_runtime_parachains::{
|
|||||||
dmp,
|
dmp,
|
||||||
ump::{self, MessageId, UmpSink, XcmSink},
|
ump::{self, MessageId, UmpSink, XcmSink},
|
||||||
};
|
};
|
||||||
pub use xcm::{v0::prelude::*, VersionedXcm};
|
pub use xcm::{latest::prelude::*, VersionedXcm};
|
||||||
pub use xcm_executor::XcmExecutor;
|
pub use xcm_executor::XcmExecutor;
|
||||||
|
|
||||||
pub trait TestExt {
|
pub trait TestExt {
|
||||||
|
|||||||
Reference in New Issue
Block a user