mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-20 23:21:02 +00:00
XCM v1 (#2815)
* MultiAsset TWO * Draft next MultiAsset API. * XCM core builds * XCM Executor builds * XCM Builder builds * API changes making their way throughout * Some TODOs * Further build fixes * Basic compile builds * First test fixed * All executor tests fixed * Typo * Optimize subsume_assets and add test * Optimize checked_sub * XCM Builder first test fixed * Fix builder tests * Fix doc test * fix some doc tests * spelling * named fields for AllOf * Update xcm/src/v0/multiasset.rs Co-authored-by: Alexander Popiak <alexander.popiak@parity.io> * Update xcm/src/v0/multiasset.rs Co-authored-by: Alexander Popiak <alexander.popiak@parity.io> * Update xcm/src/v0/multiasset.rs Co-authored-by: Alexander Popiak <alexander.popiak@parity.io> * Update xcm/src/v0/multiasset.rs Co-authored-by: Alexander Popiak <alexander.popiak@parity.io> * Reformat * Move to XCM version 1 * Spelling * warnings * Replace some more v0->v1s * warnings * format * Add max_assets param * building * test fixes * tests * another test * final test * tests * Rename Null -> Here * Introduce * More ergonomics * More ergonomics * test fix * test fixes * docs * BuyExecution includes * Fix XCM extrinsics * fmt * Make Vec<MultiAsset>/MultiAssets conversions safe * More MultiAssets conversion safety * spelling * fix doc test * Apply suggestions from code review Co-authored-by: Amar Singh <asinghchrony@protonmail.com> * Apply suggestions from code review Co-authored-by: Amar Singh <asinghchrony@protonmail.com> * fmt * Add v0, remove VersionedMultiAsset * Remove VersionedMultiLocation * Update xcm/src/v1/order.rs Co-authored-by: Amar Singh <asinghchrony@protonmail.com> * Update xcm/src/v1/mod.rs Co-authored-by: Amar Singh <asinghchrony@protonmail.com> * XCM v0 backwards compatibility * Full compatibility * fmt * Update xcm/pallet-xcm/src/lib.rs * Update xcm/src/v0/order.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Tweaks to versioning system * Fixes * fmt * Update xcm/xcm-executor/src/assets.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Update xcm/xcm-executor/src/assets.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Grumbles * Update xcm/src/v1/multiasset.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * fmt * Update xcm/src/v1/multiasset.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Update xcm/src/v1/multiasset.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Fixes * Formatting Co-authored-by: Alexander Popiak <alexander.popiak@parity.io> Co-authored-by: Amar Singh <asinghchrony@protonmail.com> Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
+108
-13
@@ -23,14 +23,32 @@
|
||||
#![no_std]
|
||||
extern crate alloc;
|
||||
|
||||
use core::{
|
||||
convert::{TryFrom, TryInto},
|
||||
result::Result,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use parity_scale_codec::{Decode, Encode, Error as CodecError, Input};
|
||||
|
||||
pub mod v0;
|
||||
pub mod v1;
|
||||
|
||||
pub mod latest {
|
||||
pub use super::v1::*;
|
||||
}
|
||||
|
||||
mod double_encoded;
|
||||
pub use double_encoded::DoubleEncoded;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub enum Unsupported {}
|
||||
impl Encode for Unsupported {}
|
||||
impl Decode for Unsupported {
|
||||
fn decode<I: Input>(_: &mut I) -> Result<Self, CodecError> {
|
||||
Err("Not decodable".into())
|
||||
}
|
||||
}
|
||||
|
||||
/// A single XCM message, together with its version code.
|
||||
#[derive(Derivative, Encode, Decode)]
|
||||
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
|
||||
@@ -38,8 +56,87 @@ pub use double_encoded::DoubleEncoded;
|
||||
#[codec(decode_bound())]
|
||||
pub enum VersionedXcm<Call> {
|
||||
V0(v0::Xcm<Call>),
|
||||
V1(v1::Xcm<Call>),
|
||||
}
|
||||
|
||||
impl<Call> From<v0::Xcm<Call>> for VersionedXcm<Call> {
|
||||
fn from(x: v0::Xcm<Call>) -> Self {
|
||||
VersionedXcm::V0(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call> From<v1::Xcm<Call>> for VersionedXcm<Call> {
|
||||
fn from(x: v1::Xcm<Call>) -> Self {
|
||||
VersionedXcm::V1(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call> TryFrom<VersionedXcm<Call>> for v0::Xcm<Call> {
|
||||
type Error = ();
|
||||
fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
|
||||
match x {
|
||||
VersionedXcm::V0(x) => Ok(x),
|
||||
VersionedXcm::V1(x) => x.try_into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call> TryFrom<VersionedXcm<Call>> for v1::Xcm<Call> {
|
||||
type Error = ();
|
||||
fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
|
||||
match x {
|
||||
VersionedXcm::V0(x) => x.try_into(),
|
||||
VersionedXcm::V1(x) => Ok(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert an `Xcm` datum into a `VersionedXcm`, based on a destination `MultiLocation` which will interpret it.
|
||||
pub trait WrapVersion {
|
||||
fn wrap_version<Call>(
|
||||
dest: &latest::MultiLocation,
|
||||
xcm: impl Into<VersionedXcm<Call>>,
|
||||
) -> Result<VersionedXcm<Call>, ()>;
|
||||
}
|
||||
|
||||
/// `()` implementation does nothing with the XCM, just sending with whatever version it was authored as.
|
||||
impl WrapVersion for () {
|
||||
fn wrap_version<Call>(
|
||||
_: &latest::MultiLocation,
|
||||
xcm: impl Into<VersionedXcm<Call>>,
|
||||
) -> Result<VersionedXcm<Call>, ()> {
|
||||
Ok(xcm.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// `WrapVersion` implementation which attempts to always convert the XCM to version 0 before wrapping it.
|
||||
pub struct AlwaysV0;
|
||||
impl WrapVersion for AlwaysV0 {
|
||||
fn wrap_version<Call>(
|
||||
_: &latest::MultiLocation,
|
||||
xcm: impl Into<VersionedXcm<Call>>,
|
||||
) -> Result<VersionedXcm<Call>, ()> {
|
||||
Ok(VersionedXcm::<Call>::V0(xcm.into().try_into()?))
|
||||
}
|
||||
}
|
||||
|
||||
/// `WrapVersion` implementation which attempts to always convert the XCM to version 1 before wrapping it.
|
||||
pub struct AlwaysV1;
|
||||
impl WrapVersion for AlwaysV1 {
|
||||
fn wrap_version<Call>(
|
||||
_: &latest::MultiLocation,
|
||||
xcm: impl Into<VersionedXcm<Call>>,
|
||||
) -> Result<VersionedXcm<Call>, ()> {
|
||||
Ok(VersionedXcm::<Call>::V1(xcm.into().try_into()?))
|
||||
}
|
||||
}
|
||||
|
||||
/// `WrapVersion` implementation which attempts to always convert the XCM to the latest version before wrapping it.
|
||||
pub type AlwaysLatest = AlwaysV1;
|
||||
|
||||
/// `WrapVersion` implementation which attempts to always convert the XCM to the release version before wrapping it.
|
||||
pub type AlwaysRelease = AlwaysV0;
|
||||
|
||||
pub mod opaque {
|
||||
pub mod v0 {
|
||||
// Everything from v0
|
||||
@@ -47,19 +144,17 @@ pub mod opaque {
|
||||
// Then override with the opaque types in v0
|
||||
pub use crate::v0::opaque::{Order, Xcm};
|
||||
}
|
||||
pub mod v1 {
|
||||
// Everything from v1
|
||||
pub use crate::v1::*;
|
||||
// Then override with the opaque types in v1
|
||||
pub use crate::v1::opaque::{Order, Xcm};
|
||||
}
|
||||
|
||||
pub mod latest {
|
||||
pub use super::v1::*;
|
||||
}
|
||||
|
||||
/// The basic `VersionedXcm` type which just uses the `Vec<u8>` as an encoded call.
|
||||
pub type VersionedXcm = super::VersionedXcm<()>;
|
||||
}
|
||||
|
||||
/// A versioned multi-location, a relative location of a cross-consensus system identifier.
|
||||
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)]
|
||||
pub enum VersionedMultiLocation {
|
||||
V0(v0::MultiLocation),
|
||||
}
|
||||
|
||||
/// A versioned multi-asset, an identifier for an asset within a consensus system.
|
||||
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)]
|
||||
pub enum VersionedMultiAsset {
|
||||
V0(v0::MultiAsset),
|
||||
}
|
||||
|
||||
+63
-53
@@ -16,18 +16,20 @@
|
||||
|
||||
//! Version 0 of the Cross-Consensus Message format data structures.
|
||||
|
||||
use crate::{DoubleEncoded, VersionedMultiAsset, VersionedXcm};
|
||||
use crate::DoubleEncoded;
|
||||
use alloc::vec::Vec;
|
||||
use core::{convert::TryFrom, fmt::Debug, result};
|
||||
use core::{
|
||||
convert::{TryFrom, TryInto},
|
||||
result,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use parity_scale_codec::{self, Decode, Encode};
|
||||
|
||||
mod junction;
|
||||
mod multi_asset;
|
||||
mod multi_location;
|
||||
mod order;
|
||||
mod traits;
|
||||
pub use junction::{BodyId, BodyPart, Junction, NetworkId};
|
||||
use super::v1::Xcm as Xcm1;
|
||||
pub use multi_asset::{AssetInstance, MultiAsset};
|
||||
pub use multi_location::MultiLocation;
|
||||
pub use order::Order;
|
||||
@@ -36,7 +38,6 @@ pub use traits::{Error, ExecuteXcm, Outcome, Result, SendXcm};
|
||||
/// A prelude for importing all types typically used when interacting with XCM messages.
|
||||
pub mod prelude {
|
||||
pub use super::{
|
||||
junction::{BodyId, BodyPart, Junction::*, NetworkId},
|
||||
multi_asset::{
|
||||
AssetInstance::{self, *},
|
||||
MultiAsset::{self, *},
|
||||
@@ -44,43 +45,14 @@ pub mod prelude {
|
||||
multi_location::MultiLocation::{self, *},
|
||||
order::Order::{self, *},
|
||||
traits::{Error as XcmError, ExecuteXcm, Outcome, Result as XcmResult, SendXcm},
|
||||
OriginKind,
|
||||
BodyId, BodyPart,
|
||||
Junction::*,
|
||||
NetworkId, OriginKind,
|
||||
Xcm::{self, *},
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: #2841 #XCMENCODE Efficient encodings for Vec<MultiAsset>, Vec<Order>, using initial byte values 128+ to encode
|
||||
// the number of items in the vector.
|
||||
|
||||
/// Basically just the XCM (more general) version of `ParachainDispatchOrigin`.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug)]
|
||||
pub enum OriginKind {
|
||||
/// Origin should just be the native dispatch origin representation for the sender in the
|
||||
/// local runtime framework. For Cumulus/Frame chains this is the `Parachain` or `Relay` origin
|
||||
/// if coming from a chain, though there may be others if the `MultiLocation` XCM origin has a
|
||||
/// primary/native dispatch origin form.
|
||||
Native,
|
||||
|
||||
/// Origin should just be the standard account-based origin with the sovereign account of
|
||||
/// the sender. For Cumulus/Frame chains, this is the `Signed` origin.
|
||||
SovereignAccount,
|
||||
|
||||
/// Origin should be the super-user. For Cumulus/Frame chains, this is the `Root` origin.
|
||||
/// This will not usually be an available option.
|
||||
Superuser,
|
||||
|
||||
/// Origin should be interpreted as an XCM native origin and the `MultiLocation` should be
|
||||
/// encoded directly in the dispatch origin unchanged. For Cumulus/Frame chains, this will be
|
||||
/// the `pallet_xcm::Origin::Xcm` type.
|
||||
Xcm,
|
||||
}
|
||||
|
||||
/// Response data to a query.
|
||||
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)]
|
||||
pub enum Response {
|
||||
/// Some assets.
|
||||
Assets(Vec<MultiAsset>),
|
||||
}
|
||||
pub use super::v1::{BodyId, BodyPart, Junction, NetworkId, OriginKind, Response};
|
||||
|
||||
/// Cross-Consensus Message: A message from one consensus system to another.
|
||||
///
|
||||
@@ -275,21 +247,6 @@ pub enum Xcm<Call> {
|
||||
RelayedFrom { who: MultiLocation, message: alloc::boxed::Box<Xcm<Call>> },
|
||||
}
|
||||
|
||||
impl<Call> From<Xcm<Call>> for VersionedXcm<Call> {
|
||||
fn from(x: Xcm<Call>) -> Self {
|
||||
VersionedXcm::V0(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call> TryFrom<VersionedXcm<Call>> for Xcm<Call> {
|
||||
type Error = ();
|
||||
fn try_from(x: VersionedXcm<Call>) -> result::Result<Self, ()> {
|
||||
match x {
|
||||
VersionedXcm::V0(x) => Ok(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call> Xcm<Call> {
|
||||
pub fn into<C>(self) -> Xcm<C> {
|
||||
Xcm::from(self)
|
||||
@@ -329,3 +286,56 @@ pub mod opaque {
|
||||
|
||||
pub use super::order::opaque::*;
|
||||
}
|
||||
|
||||
impl<Call> TryFrom<Xcm1<Call>> for Xcm<Call> {
|
||||
type Error = ();
|
||||
fn try_from(x: Xcm1<Call>) -> result::Result<Xcm<Call>, ()> {
|
||||
use Xcm::*;
|
||||
Ok(match x {
|
||||
Xcm1::WithdrawAsset { assets, effects } => WithdrawAsset {
|
||||
assets: assets.into(),
|
||||
effects: effects
|
||||
.into_iter()
|
||||
.map(Order::try_from)
|
||||
.collect::<result::Result<_, _>>()?,
|
||||
},
|
||||
Xcm1::ReserveAssetDeposited { assets, effects } => ReserveAssetDeposit {
|
||||
assets: assets.into(),
|
||||
effects: effects
|
||||
.into_iter()
|
||||
.map(Order::try_from)
|
||||
.collect::<result::Result<_, _>>()?,
|
||||
},
|
||||
Xcm1::ReceiveTeleportedAsset { assets, effects } => TeleportAsset {
|
||||
assets: assets.into(),
|
||||
effects: effects
|
||||
.into_iter()
|
||||
.map(Order::try_from)
|
||||
.collect::<result::Result<_, _>>()?,
|
||||
},
|
||||
Xcm1::QueryResponse { query_id: u64, response } =>
|
||||
QueryResponse { query_id: u64, response },
|
||||
Xcm1::TransferAsset { assets, beneficiary } =>
|
||||
TransferAsset { assets: assets.into(), dest: beneficiary.into() },
|
||||
Xcm1::TransferReserveAsset { assets, dest, effects } => TransferReserveAsset {
|
||||
assets: assets.into(),
|
||||
dest: dest.into(),
|
||||
effects: effects
|
||||
.into_iter()
|
||||
.map(Order::try_from)
|
||||
.collect::<result::Result<_, _>>()?,
|
||||
},
|
||||
Xcm1::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
|
||||
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
|
||||
Xcm1::HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
|
||||
Xcm1::HrmpChannelClosing { initiator, sender, recipient } =>
|
||||
HrmpChannelClosing { initiator, sender, recipient },
|
||||
Xcm1::Transact { origin_type, require_weight_at_most, call } =>
|
||||
Transact { origin_type, require_weight_at_most, call: call.into() },
|
||||
Xcm1::RelayedFrom { who, message } => RelayedFrom {
|
||||
who: who.into(),
|
||||
message: alloc::boxed::Box::new((*message).try_into()?),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,40 +16,12 @@
|
||||
|
||||
//! Cross-Consensus Message format data structures.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::{convert::TryFrom, result};
|
||||
|
||||
use super::{MultiLocation, VersionedMultiAsset};
|
||||
use super::MultiLocation;
|
||||
use crate::v1::{MultiAsset as MultiAsset1, MultiAssetFilter, MultiAssets, WildMultiAsset};
|
||||
use alloc::{vec, vec::Vec};
|
||||
use parity_scale_codec::{self, Decode, Encode};
|
||||
|
||||
/// A general identifier for an instance of a non-fungible asset class.
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)]
|
||||
pub enum AssetInstance {
|
||||
/// Undefined - used if the NFA class has only one instance.
|
||||
Undefined,
|
||||
|
||||
/// A compact index. Technically this could be greater than `u128`, but this implementation supports only
|
||||
/// values up to `2**128 - 1`.
|
||||
Index {
|
||||
#[codec(compact)]
|
||||
id: u128,
|
||||
},
|
||||
|
||||
/// A 4-byte fixed-length datum.
|
||||
Array4([u8; 4]),
|
||||
|
||||
/// An 8-byte fixed-length datum.
|
||||
Array8([u8; 8]),
|
||||
|
||||
/// A 16-byte fixed-length datum.
|
||||
Array16([u8; 16]),
|
||||
|
||||
/// A 32-byte fixed-length datum.
|
||||
Array32([u8; 32]),
|
||||
|
||||
/// An arbitrary piece of data. Use only when necessary.
|
||||
Blob(Vec<u8>),
|
||||
}
|
||||
pub use crate::v1::AssetInstance;
|
||||
|
||||
/// A single general identifier for an asset.
|
||||
///
|
||||
@@ -318,17 +290,53 @@ impl MultiAsset {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultiAsset> for VersionedMultiAsset {
|
||||
fn from(x: MultiAsset) -> Self {
|
||||
VersionedMultiAsset::V0(x)
|
||||
impl From<MultiAsset1> for MultiAsset {
|
||||
fn from(a: MultiAsset1) -> MultiAsset {
|
||||
use crate::v1::{AssetId::*, Fungibility::*};
|
||||
use MultiAsset::*;
|
||||
match (a.id, a.fun) {
|
||||
(Concrete(id), Fungible(amount)) => ConcreteFungible { id: id.into(), amount },
|
||||
(Concrete(class), NonFungible(instance)) =>
|
||||
ConcreteNonFungible { class: class.into(), instance },
|
||||
(Abstract(id), Fungible(amount)) => AbstractFungible { id, amount },
|
||||
(Abstract(class), NonFungible(instance)) => AbstractNonFungible { class, instance },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<VersionedMultiAsset> for MultiAsset {
|
||||
type Error = ();
|
||||
fn try_from(x: VersionedMultiAsset) -> result::Result<Self, ()> {
|
||||
match x {
|
||||
VersionedMultiAsset::V0(x) => Ok(x),
|
||||
impl From<MultiAssets> for Vec<MultiAsset> {
|
||||
fn from(a: MultiAssets) -> Vec<MultiAsset> {
|
||||
a.drain().into_iter().map(MultiAsset::from).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WildMultiAsset> for MultiAsset {
|
||||
fn from(a: WildMultiAsset) -> MultiAsset {
|
||||
use crate::v1::{AssetId::*, WildFungibility::*};
|
||||
use MultiAsset::*;
|
||||
match a {
|
||||
WildMultiAsset::All => All,
|
||||
WildMultiAsset::AllOf { id, fun } => match (id, fun) {
|
||||
(Concrete(id), Fungible) => AllConcreteFungible { id: id.into() },
|
||||
(Concrete(class), NonFungible) => AllConcreteNonFungible { class: class.into() },
|
||||
(Abstract(id), Fungible) => AllAbstractFungible { id },
|
||||
(Abstract(class), NonFungible) => AllAbstractNonFungible { class },
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WildMultiAsset> for Vec<MultiAsset> {
|
||||
fn from(a: WildMultiAsset) -> Vec<MultiAsset> {
|
||||
vec![a.into()]
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultiAssetFilter> for Vec<MultiAsset> {
|
||||
fn from(a: MultiAssetFilter) -> Vec<MultiAsset> {
|
||||
match a {
|
||||
MultiAssetFilter::Definite(assets) => assets.into(),
|
||||
MultiAssetFilter::Wild(wildcard) => wildcard.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -367,29 +375,20 @@ mod tests {
|
||||
.contains(&AbstractFungible { id: vec![99u8], amount: 100 }));
|
||||
|
||||
// For non-fungibles, containing is equality.
|
||||
assert!(!AbstractNonFungible {
|
||||
class: vec![99u8],
|
||||
instance: AssetInstance::Index { id: 9 }
|
||||
}
|
||||
.contains(&AbstractNonFungible {
|
||||
class: vec![98u8],
|
||||
instance: AssetInstance::Index { id: 9 }
|
||||
}));
|
||||
assert!(!AbstractNonFungible {
|
||||
class: vec![99u8],
|
||||
instance: AssetInstance::Index { id: 8 }
|
||||
}
|
||||
.contains(&AbstractNonFungible {
|
||||
class: vec![99u8],
|
||||
instance: AssetInstance::Index { id: 9 }
|
||||
}));
|
||||
assert!(AbstractNonFungible {
|
||||
class: vec![99u8],
|
||||
instance: AssetInstance::Index { id: 9 }
|
||||
}
|
||||
.contains(&AbstractNonFungible {
|
||||
class: vec![99u8],
|
||||
instance: AssetInstance::Index { id: 9 }
|
||||
}));
|
||||
assert!(!AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(9) }
|
||||
.contains(&AbstractNonFungible {
|
||||
class: vec![98u8],
|
||||
instance: AssetInstance::Index(9)
|
||||
}));
|
||||
assert!(!AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(8) }
|
||||
.contains(&AbstractNonFungible {
|
||||
class: vec![99u8],
|
||||
instance: AssetInstance::Index(9)
|
||||
}));
|
||||
assert!(AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(9) }
|
||||
.contains(&AbstractNonFungible {
|
||||
class: vec![99u8],
|
||||
instance: AssetInstance::Index(9)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,9 @@
|
||||
|
||||
//! Cross-Consensus Message format data structures.
|
||||
|
||||
use core::{convert::TryFrom, mem, result};
|
||||
use core::{mem, result};
|
||||
|
||||
use super::Junction;
|
||||
use crate::VersionedMultiLocation;
|
||||
use super::{super::v1::MultiLocation as MultiLocation1, Junction};
|
||||
use parity_scale_codec::{self, Decode, Encode};
|
||||
|
||||
/// A relative path between state-bearing consensus systems.
|
||||
@@ -697,17 +696,20 @@ impl MultiLocation {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultiLocation> for VersionedMultiLocation {
|
||||
fn from(x: MultiLocation) -> Self {
|
||||
VersionedMultiLocation::V0(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<VersionedMultiLocation> for MultiLocation {
|
||||
type Error = ();
|
||||
fn try_from(x: VersionedMultiLocation) -> result::Result<Self, ()> {
|
||||
match x {
|
||||
VersionedMultiLocation::V0(x) => Ok(x),
|
||||
impl From<MultiLocation1> for MultiLocation {
|
||||
fn from(old: MultiLocation1) -> Self {
|
||||
use MultiLocation::*;
|
||||
match old {
|
||||
MultiLocation1::Here => Null,
|
||||
MultiLocation1::X1(j0) => X1(j0),
|
||||
MultiLocation1::X2(j0, j1) => X2(j0, j1),
|
||||
MultiLocation1::X3(j0, j1, j2) => X3(j0, j1, j2),
|
||||
MultiLocation1::X4(j0, j1, j2, j3) => X4(j0, j1, j2, j3),
|
||||
MultiLocation1::X5(j0, j1, j2, j3, j4) => X5(j0, j1, j2, j3, j4),
|
||||
MultiLocation1::X6(j0, j1, j2, j3, j4, j5) => X6(j0, j1, j2, j3, j4, j5),
|
||||
MultiLocation1::X7(j0, j1, j2, j3, j4, j5, j6) => X7(j0, j1, j2, j3, j4, j5, j6),
|
||||
MultiLocation1::X8(j0, j1, j2, j3, j4, j5, j6, j7) =>
|
||||
X8(j0, j1, j2, j3, j4, j5, j6, j7),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
|
||||
//! Version 0 of the Cross-Consensus Message format data structures.
|
||||
|
||||
use super::{MultiAsset, MultiLocation, Xcm};
|
||||
use super::{super::v1::Order as Order1, MultiAsset, MultiLocation, Xcm};
|
||||
use alloc::vec::Vec;
|
||||
use core::{convert::TryFrom, result};
|
||||
use derivative::Derivative;
|
||||
use parity_scale_codec::{self, Decode, Encode};
|
||||
|
||||
@@ -152,3 +153,54 @@ impl<Call> Order<Call> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call> TryFrom<Order1<Call>> for Order<Call> {
|
||||
type Error = ();
|
||||
fn try_from(old: Order1<Call>) -> result::Result<Order<Call>, ()> {
|
||||
use Order::*;
|
||||
Ok(match old {
|
||||
Order1::Noop => Null,
|
||||
Order1::DepositAsset { assets, beneficiary, .. } =>
|
||||
DepositAsset { assets: assets.into(), dest: beneficiary.into() },
|
||||
Order1::DepositReserveAsset { assets, dest, effects, .. } => DepositReserveAsset {
|
||||
assets: assets.into(),
|
||||
dest: dest.into(),
|
||||
effects: effects
|
||||
.into_iter()
|
||||
.map(Order::<()>::try_from)
|
||||
.collect::<result::Result<_, _>>()?,
|
||||
},
|
||||
Order1::ExchangeAsset { give, receive } =>
|
||||
ExchangeAsset { give: give.into(), receive: receive.into() },
|
||||
Order1::InitiateReserveWithdraw { assets, reserve, effects } =>
|
||||
InitiateReserveWithdraw {
|
||||
assets: assets.into(),
|
||||
reserve: reserve.into(),
|
||||
effects: effects
|
||||
.into_iter()
|
||||
.map(Order::<()>::try_from)
|
||||
.collect::<result::Result<_, _>>()?,
|
||||
},
|
||||
Order1::InitiateTeleport { assets, dest, effects } => InitiateTeleport {
|
||||
assets: assets.into(),
|
||||
dest: dest.into(),
|
||||
effects: effects
|
||||
.into_iter()
|
||||
.map(Order::<()>::try_from)
|
||||
.collect::<result::Result<_, _>>()?,
|
||||
},
|
||||
Order1::QueryHolding { query_id, dest, assets } =>
|
||||
QueryHolding { query_id, dest: dest.into(), assets: assets.into() },
|
||||
Order1::BuyExecution { fees, weight, debt, halt_on_error, orders, instructions } => {
|
||||
if !orders.is_empty() {
|
||||
return Err(())
|
||||
}
|
||||
let xcm = instructions
|
||||
.into_iter()
|
||||
.map(Xcm::<Call>::try_from)
|
||||
.collect::<result::Result<_, _>>()?;
|
||||
BuyExecution { fees: fees.into(), weight, debt, halt_on_error, xcm }
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,11 +40,7 @@ pub enum BodyId {
|
||||
/// A named body.
|
||||
Named(Vec<u8>),
|
||||
/// An indexed body.
|
||||
// TODO: parity-scale-codec#262: Change to be a tuple.
|
||||
Index {
|
||||
#[codec(compact)]
|
||||
id: u32,
|
||||
},
|
||||
Index(#[codec(compact)] u32),
|
||||
/// The unambiguous executive body (for Polkadot, this would be the Polkadot council).
|
||||
Executive,
|
||||
/// The unambiguous technical body (for Polkadot, this would be the Technical Committee).
|
||||
@@ -0,0 +1,392 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Cumulus.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Version 1 of the Cross-Consensus Message format data structures.
|
||||
|
||||
use super::v0::Xcm as Xcm0;
|
||||
use crate::DoubleEncoded;
|
||||
use alloc::vec::Vec;
|
||||
use core::{
|
||||
convert::{TryFrom, TryInto},
|
||||
fmt::Debug,
|
||||
result,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use parity_scale_codec::{self, Decode, Encode};
|
||||
|
||||
mod junction;
|
||||
pub mod multiasset;
|
||||
mod multilocation;
|
||||
mod order;
|
||||
mod traits; // the new multiasset.
|
||||
|
||||
pub use junction::{BodyId, BodyPart, Junction, NetworkId};
|
||||
pub use multiasset::{
|
||||
AssetId, AssetInstance, Fungibility, MultiAsset, MultiAssetFilter, MultiAssets,
|
||||
WildFungibility, WildMultiAsset,
|
||||
};
|
||||
pub use multilocation::MultiLocation;
|
||||
pub use order::Order;
|
||||
pub use traits::{Error, ExecuteXcm, Outcome, Result, SendXcm};
|
||||
|
||||
/// A prelude for importing all types typically used when interacting with XCM messages.
|
||||
pub mod prelude {
|
||||
pub use super::{
|
||||
junction::{
|
||||
BodyId, BodyPart,
|
||||
Junction::*,
|
||||
NetworkId::{self, *},
|
||||
},
|
||||
multiasset::{
|
||||
AssetId::{self, *},
|
||||
AssetInstance::{self, *},
|
||||
Fungibility::{self, *},
|
||||
MultiAsset,
|
||||
MultiAssetFilter::{self, *},
|
||||
MultiAssets,
|
||||
WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible},
|
||||
WildMultiAsset::{self, *},
|
||||
},
|
||||
multilocation::MultiLocation::{self, *},
|
||||
opaque,
|
||||
order::Order::{self, *},
|
||||
traits::{Error as XcmError, ExecuteXcm, Outcome, Result as XcmResult, SendXcm},
|
||||
OriginKind, Response,
|
||||
Xcm::{self, *},
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: #2841 #XCMENCODE Efficient encodings for MultiAssets, Vec<Order>, using initial byte values 128+ to encode
|
||||
// the number of items in the vector.
|
||||
|
||||
/// Basically just the XCM (more general) version of `ParachainDispatchOrigin`.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug)]
|
||||
pub enum OriginKind {
|
||||
/// Origin should just be the native dispatch origin representation for the sender in the
|
||||
/// local runtime framework. For Cumulus/Frame chains this is the `Parachain` or `Relay` origin
|
||||
/// if coming from a chain, though there may be others if the `MultiLocation` XCM origin has a
|
||||
/// primary/native dispatch origin form.
|
||||
Native,
|
||||
|
||||
/// Origin should just be the standard account-based origin with the sovereign account of
|
||||
/// the sender. For Cumulus/Frame chains, this is the `Signed` origin.
|
||||
SovereignAccount,
|
||||
|
||||
/// Origin should be the super-user. For Cumulus/Frame chains, this is the `Root` origin.
|
||||
/// This will not usually be an available option.
|
||||
Superuser,
|
||||
|
||||
/// Origin should be interpreted as an XCM native origin and the `MultiLocation` should be
|
||||
/// encoded directly in the dispatch origin unchanged. For Cumulus/Frame chains, this will be
|
||||
/// the `pallet_xcm::Origin::Xcm` type.
|
||||
Xcm,
|
||||
}
|
||||
|
||||
/// Response data to a query.
|
||||
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)]
|
||||
pub enum Response {
|
||||
/// Some assets.
|
||||
Assets(MultiAssets),
|
||||
}
|
||||
|
||||
/// Cross-Consensus Message: A message from one consensus system to another.
|
||||
///
|
||||
/// Consensus systems that may send and receive messages include blockchains and smart contracts.
|
||||
///
|
||||
/// All messages are delivered from a known *origin*, expressed as a `MultiLocation`.
|
||||
///
|
||||
/// This is the inner XCM format and is version-sensitive. Messages are typically passed using the outer
|
||||
/// XCM format, known as `VersionedXcm`.
|
||||
#[derive(Derivative, Encode, Decode)]
|
||||
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
|
||||
#[codec(encode_bound())]
|
||||
#[codec(decode_bound())]
|
||||
pub enum Xcm<Call> {
|
||||
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place them into `holding`. Execute the
|
||||
/// orders (`effects`).
|
||||
///
|
||||
/// - `assets`: The asset(s) to be withdrawn into holding.
|
||||
/// - `effects`: The order(s) to execute on the holding register.
|
||||
///
|
||||
/// Kind: *Instruction*.
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 0)]
|
||||
WithdrawAsset { assets: MultiAssets, effects: Vec<Order<Call>> },
|
||||
|
||||
/// Asset(s) (`assets`) have been received into the ownership of this system on the `origin` system.
|
||||
///
|
||||
/// Some orders are given (`effects`) which should be executed once the corresponding derivative assets have
|
||||
/// been placed into `holding`.
|
||||
///
|
||||
/// - `assets`: The asset(s) that are minted into holding.
|
||||
/// - `effects`: The order(s) to execute on the holding register.
|
||||
///
|
||||
/// Safety: `origin` must be trusted to have received and be storing `assets` such that they may later be
|
||||
/// withdrawn should this system send a corresponding message.
|
||||
///
|
||||
/// Kind: *Trusted Indication*.
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 1)]
|
||||
ReserveAssetDeposited { assets: MultiAssets, effects: Vec<Order<Call>> },
|
||||
|
||||
/// Asset(s) (`assets`) have been destroyed on the `origin` system and equivalent assets should be
|
||||
/// created on this system.
|
||||
///
|
||||
/// Some orders are given (`effects`) which should be executed once the corresponding derivative assets have
|
||||
/// been placed into the Holding Register.
|
||||
///
|
||||
/// - `assets`: The asset(s) that are minted into the Holding Register.
|
||||
/// - `effects`: The order(s) to execute on the Holding Register.
|
||||
///
|
||||
/// Safety: `origin` must be trusted to have irrevocably destroyed the corresponding `assets` prior as a consequence
|
||||
/// of sending this message.
|
||||
///
|
||||
/// Kind: *Trusted Indication*.
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 2)]
|
||||
ReceiveTeleportedAsset { assets: MultiAssets, effects: Vec<Order<Call>> },
|
||||
|
||||
/// Indication of the contents of the holding register corresponding to the `QueryHolding` order of `query_id`.
|
||||
///
|
||||
/// - `query_id`: The identifier of the query that resulted in this message being sent.
|
||||
/// - `assets`: The message content.
|
||||
///
|
||||
/// Safety: No concerns.
|
||||
///
|
||||
/// Kind: *Information*.
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 3)]
|
||||
QueryResponse {
|
||||
#[codec(compact)]
|
||||
query_id: u64,
|
||||
response: Response,
|
||||
},
|
||||
|
||||
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the
|
||||
/// ownership of `beneficiary`.
|
||||
///
|
||||
/// - `assets`: The asset(s) to be withdrawn.
|
||||
/// - `beneficiary`: The new owner for the assets.
|
||||
///
|
||||
/// Safety: No concerns.
|
||||
///
|
||||
/// Kind: *Instruction*.
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 4)]
|
||||
TransferAsset { assets: MultiAssets, beneficiary: MultiLocation },
|
||||
|
||||
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the
|
||||
/// ownership of `dest` within this consensus system (i.e. its sovereign account).
|
||||
///
|
||||
/// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`.
|
||||
///
|
||||
/// - `assets`: The asset(s) to be withdrawn.
|
||||
/// - `dest`: The location whose sovereign account will own the assets and thus the effective beneficiary for the
|
||||
/// assets and the notification target for the reserve asset deposit message.
|
||||
/// - `effects`: The orders that should be contained in the `ReserveAssetDeposited` which is sent onwards to
|
||||
/// `dest`.
|
||||
///
|
||||
/// Safety: No concerns.
|
||||
///
|
||||
/// Kind: *Instruction*.
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 5)]
|
||||
TransferReserveAsset { assets: MultiAssets, dest: MultiLocation, effects: Vec<Order<()>> },
|
||||
|
||||
/// Apply the encoded transaction `call`, whose dispatch-origin should be `origin` as expressed by the kind
|
||||
/// of origin `origin_type`.
|
||||
///
|
||||
/// - `origin_type`: The means of expressing the message origin as a dispatch origin.
|
||||
/// - `max_weight`: The weight of `call`; this should be at least the chain's calculated weight and will
|
||||
/// be used in the weight determination arithmetic.
|
||||
/// - `call`: The encoded transaction to be applied.
|
||||
///
|
||||
/// Safety: No concerns.
|
||||
///
|
||||
/// Kind: *Instruction*.
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 6)]
|
||||
Transact { origin_type: OriginKind, require_weight_at_most: u64, call: DoubleEncoded<Call> },
|
||||
|
||||
/// A message to notify about a new incoming HRMP channel. This message is meant to be sent by the
|
||||
/// relay-chain to a para.
|
||||
///
|
||||
/// - `sender`: The sender in the to-be opened channel. Also, the initiator of the channel opening.
|
||||
/// - `max_message_size`: The maximum size of a message proposed by the sender.
|
||||
/// - `max_capacity`: The maximum number of messages that can be queued in the channel.
|
||||
///
|
||||
/// Safety: The message should originate directly from the relay-chain.
|
||||
///
|
||||
/// Kind: *System Notification*
|
||||
#[codec(index = 7)]
|
||||
HrmpNewChannelOpenRequest {
|
||||
#[codec(compact)]
|
||||
sender: u32,
|
||||
#[codec(compact)]
|
||||
max_message_size: u32,
|
||||
#[codec(compact)]
|
||||
max_capacity: u32,
|
||||
},
|
||||
|
||||
/// A message to notify about that a previously sent open channel request has been accepted by
|
||||
/// the recipient. That means that the channel will be opened during the next relay-chain session
|
||||
/// change. This message is meant to be sent by the relay-chain to a para.
|
||||
///
|
||||
/// Safety: The message should originate directly from the relay-chain.
|
||||
///
|
||||
/// Kind: *System Notification*
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 8)]
|
||||
HrmpChannelAccepted {
|
||||
#[codec(compact)]
|
||||
recipient: u32,
|
||||
},
|
||||
|
||||
/// A message to notify that the other party in an open channel decided to close it. In particular,
|
||||
/// `initiator` is going to close the channel opened from `sender` to the `recipient`. The close
|
||||
/// will be enacted at the next relay-chain session change. This message is meant to be sent by
|
||||
/// the relay-chain to a para.
|
||||
///
|
||||
/// Safety: The message should originate directly from the relay-chain.
|
||||
///
|
||||
/// Kind: *System Notification*
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 9)]
|
||||
HrmpChannelClosing {
|
||||
#[codec(compact)]
|
||||
initiator: u32,
|
||||
#[codec(compact)]
|
||||
sender: u32,
|
||||
#[codec(compact)]
|
||||
recipient: u32,
|
||||
},
|
||||
|
||||
/// A message to indicate that the embedded XCM is actually arriving on behalf of some consensus
|
||||
/// location within the origin.
|
||||
///
|
||||
/// Safety: `who` must be an interior location of the context. This basically means that no `Parent`
|
||||
/// junctions are allowed in it. This should be verified at the time of XCM execution.
|
||||
///
|
||||
/// Kind: *Instruction*
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 10)]
|
||||
RelayedFrom { who: MultiLocation, message: alloc::boxed::Box<Xcm<Call>> },
|
||||
}
|
||||
|
||||
impl<Call> Xcm<Call> {
|
||||
pub fn into<C>(self) -> Xcm<C> {
|
||||
Xcm::from(self)
|
||||
}
|
||||
pub fn from<C>(xcm: Xcm<C>) -> Self {
|
||||
use Xcm::*;
|
||||
match xcm {
|
||||
WithdrawAsset { assets, effects } =>
|
||||
WithdrawAsset { assets, effects: effects.into_iter().map(Order::into).collect() },
|
||||
ReserveAssetDeposited { assets, effects } => ReserveAssetDeposited {
|
||||
assets,
|
||||
effects: effects.into_iter().map(Order::into).collect(),
|
||||
},
|
||||
ReceiveTeleportedAsset { assets, effects } => ReceiveTeleportedAsset {
|
||||
assets,
|
||||
effects: effects.into_iter().map(Order::into).collect(),
|
||||
},
|
||||
QueryResponse { query_id: u64, response } => QueryResponse { query_id: u64, response },
|
||||
TransferAsset { assets, beneficiary } => TransferAsset { assets, beneficiary },
|
||||
TransferReserveAsset { assets, dest, effects } =>
|
||||
TransferReserveAsset { assets, dest, effects },
|
||||
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
|
||||
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
|
||||
HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
|
||||
HrmpChannelClosing { initiator, sender, recipient } =>
|
||||
HrmpChannelClosing { initiator, sender, recipient },
|
||||
Transact { origin_type, require_weight_at_most, call } =>
|
||||
Transact { origin_type, require_weight_at_most, call: call.into() },
|
||||
RelayedFrom { who, message } =>
|
||||
RelayedFrom { who, message: alloc::boxed::Box::new((*message).into()) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod opaque {
|
||||
/// The basic concrete type of `generic::Xcm`, which doesn't make any assumptions about the format of a
|
||||
/// call other than it is pre-encoded.
|
||||
pub type Xcm = super::Xcm<()>;
|
||||
|
||||
pub use super::order::opaque::*;
|
||||
}
|
||||
|
||||
impl<Call> TryFrom<Xcm0<Call>> for Xcm<Call> {
|
||||
type Error = ();
|
||||
fn try_from(old: Xcm0<Call>) -> result::Result<Xcm<Call>, ()> {
|
||||
use Xcm::*;
|
||||
Ok(match old {
|
||||
Xcm0::WithdrawAsset { assets, effects } => WithdrawAsset {
|
||||
assets: assets.try_into()?,
|
||||
effects: effects
|
||||
.into_iter()
|
||||
.map(Order::try_from)
|
||||
.collect::<result::Result<_, _>>()?,
|
||||
},
|
||||
Xcm0::ReserveAssetDeposit { assets, effects } => ReserveAssetDeposited {
|
||||
assets: assets.try_into()?,
|
||||
effects: effects
|
||||
.into_iter()
|
||||
.map(Order::try_from)
|
||||
.collect::<result::Result<_, _>>()?,
|
||||
},
|
||||
Xcm0::TeleportAsset { assets, effects } => ReceiveTeleportedAsset {
|
||||
assets: assets.try_into()?,
|
||||
effects: effects
|
||||
.into_iter()
|
||||
.map(Order::try_from)
|
||||
.collect::<result::Result<_, _>>()?,
|
||||
},
|
||||
Xcm0::QueryResponse { query_id: u64, response } =>
|
||||
QueryResponse { query_id: u64, response },
|
||||
Xcm0::TransferAsset { assets, dest } =>
|
||||
TransferAsset { assets: assets.try_into()?, beneficiary: dest.into() },
|
||||
Xcm0::TransferReserveAsset { assets, dest, effects } => TransferReserveAsset {
|
||||
assets: assets.try_into()?,
|
||||
dest: dest.into(),
|
||||
effects: effects
|
||||
.into_iter()
|
||||
.map(Order::try_from)
|
||||
.collect::<result::Result<_, _>>()?,
|
||||
},
|
||||
Xcm0::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
|
||||
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
|
||||
Xcm0::HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
|
||||
Xcm0::HrmpChannelClosing { initiator, sender, recipient } =>
|
||||
HrmpChannelClosing { initiator, sender, recipient },
|
||||
Xcm0::Transact { origin_type, require_weight_at_most, call } =>
|
||||
Transact { origin_type, require_weight_at_most, call: call.into() },
|
||||
Xcm0::RelayedFrom { who, message } => RelayedFrom {
|
||||
who: who.into(),
|
||||
message: alloc::boxed::Box::new((*message).try_into()?),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,575 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Cross-Consensus Message format asset data structures.
|
||||
//!
|
||||
//! This encompasses four types for representing assets:
|
||||
//! - `MultiAsset`: A description of a single asset, either an instance of a non-fungible or some amount of a fungible.
|
||||
//! - `MultiAssets`: A collection of `MultiAsset`s. These are stored in a `Vec` and sorted with fungibles first.
|
||||
//! - `Wild`: A single asset wildcard, this can either be "all" assets, or all assets of a specific kind.
|
||||
//! - `MultiAssetFilter`: A combination of `Wild` and `MultiAssets` designed for efficiently filtering an XCM holding
|
||||
//! account.
|
||||
|
||||
use super::{
|
||||
Junction,
|
||||
MultiLocation::{self, X1},
|
||||
};
|
||||
use alloc::{vec, vec::Vec};
|
||||
use core::{
|
||||
cmp::Ordering,
|
||||
convert::{TryFrom, TryInto},
|
||||
result,
|
||||
};
|
||||
use parity_scale_codec::{self as codec, Decode, Encode};
|
||||
|
||||
/// A general identifier for an instance of a non-fungible asset class.
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)]
|
||||
pub enum AssetInstance {
|
||||
/// Undefined - used if the non-fungible asset class has only one instance.
|
||||
Undefined,
|
||||
|
||||
/// A compact index. Technically this could be greater than `u128`, but this implementation supports only
|
||||
/// values up to `2**128 - 1`.
|
||||
Index(#[codec(compact)] u128),
|
||||
|
||||
/// A 4-byte fixed-length datum.
|
||||
Array4([u8; 4]),
|
||||
|
||||
/// An 8-byte fixed-length datum.
|
||||
Array8([u8; 8]),
|
||||
|
||||
/// A 16-byte fixed-length datum.
|
||||
Array16([u8; 16]),
|
||||
|
||||
/// A 32-byte fixed-length datum.
|
||||
Array32([u8; 32]),
|
||||
|
||||
/// An arbitrary piece of data. Use only when necessary.
|
||||
Blob(Vec<u8>),
|
||||
}
|
||||
|
||||
impl From<()> for AssetInstance {
|
||||
fn from(_: ()) -> Self {
|
||||
Self::Undefined
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 4]> for AssetInstance {
|
||||
fn from(x: [u8; 4]) -> Self {
|
||||
Self::Array4(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 8]> for AssetInstance {
|
||||
fn from(x: [u8; 8]) -> Self {
|
||||
Self::Array8(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 16]> for AssetInstance {
|
||||
fn from(x: [u8; 16]) -> Self {
|
||||
Self::Array16(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 32]> for AssetInstance {
|
||||
fn from(x: [u8; 32]) -> Self {
|
||||
Self::Array32(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for AssetInstance {
|
||||
fn from(x: Vec<u8>) -> Self {
|
||||
Self::Blob(x)
|
||||
}
|
||||
}
|
||||
|
||||
/// Classification of an asset being concrete or abstract.
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)]
|
||||
pub enum AssetId {
|
||||
Concrete(MultiLocation),
|
||||
Abstract(Vec<u8>),
|
||||
}
|
||||
|
||||
impl From<MultiLocation> for AssetId {
|
||||
fn from(x: MultiLocation) -> Self {
|
||||
Self::Concrete(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Junction> for AssetId {
|
||||
fn from(x: Junction) -> Self {
|
||||
Self::Concrete(X1(x))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for AssetId {
|
||||
fn from(x: Vec<u8>) -> Self {
|
||||
Self::Abstract(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl AssetId {
|
||||
/// Prepend a `MultiLocation` to a concrete asset, giving it a new root location.
|
||||
pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
|
||||
if let AssetId::Concrete(ref mut l) = self {
|
||||
l.prepend_with(prepend.clone()).map_err(|_| ())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `MultiAsset` value.
|
||||
pub fn into_multiasset(self, fun: Fungibility) -> MultiAsset {
|
||||
MultiAsset { fun, id: self }
|
||||
}
|
||||
|
||||
/// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `WildMultiAsset`
|
||||
/// wildcard (`AllOf`) value.
|
||||
pub fn into_wild(self, fun: WildFungibility) -> WildMultiAsset {
|
||||
WildMultiAsset::AllOf { fun, id: self }
|
||||
}
|
||||
}
|
||||
|
||||
/// Classification of whether an asset is fungible or not, along with a mandatory amount or instance.
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)]
|
||||
pub enum Fungibility {
|
||||
Fungible(u128),
|
||||
NonFungible(AssetInstance),
|
||||
}
|
||||
|
||||
impl Fungibility {
|
||||
pub fn is_kind(&self, w: WildFungibility) -> bool {
|
||||
use Fungibility::*;
|
||||
use WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible};
|
||||
matches!((self, w), (Fungible(_), WildFungible) | (NonFungible(_), WildNonFungible))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u128> for Fungibility {
|
||||
fn from(amount: u128) -> Fungibility {
|
||||
debug_assert_ne!(amount, 0);
|
||||
Fungibility::Fungible(amount)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<AssetInstance>> From<T> for Fungibility {
|
||||
fn from(instance: T) -> Fungibility {
|
||||
Fungibility::NonFungible(instance.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode)]
|
||||
pub struct MultiAsset {
|
||||
pub id: AssetId,
|
||||
pub fun: Fungibility,
|
||||
}
|
||||
|
||||
impl PartialOrd for MultiAsset {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for MultiAsset {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
match (&self.fun, &other.fun) {
|
||||
(Fungibility::Fungible(..), Fungibility::NonFungible(..)) => Ordering::Less,
|
||||
(Fungibility::NonFungible(..), Fungibility::Fungible(..)) => Ordering::Greater,
|
||||
_ => (&self.id, &self.fun).cmp(&(&other.id, &other.fun)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Into<AssetId>, B: Into<Fungibility>> From<(A, B)> for MultiAsset {
|
||||
fn from((id, fun): (A, B)) -> MultiAsset {
|
||||
MultiAsset { fun: fun.into(), id: id.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiAsset {
|
||||
pub fn is_fungible(&self, maybe_id: Option<AssetId>) -> bool {
|
||||
use Fungibility::*;
|
||||
matches!(self.fun, Fungible(..)) && maybe_id.map_or(true, |i| i == self.id)
|
||||
}
|
||||
|
||||
pub fn is_non_fungible(&self, maybe_id: Option<AssetId>) -> bool {
|
||||
use Fungibility::*;
|
||||
matches!(self.fun, NonFungible(..)) && maybe_id.map_or(true, |i| i == self.id)
|
||||
}
|
||||
|
||||
/// Prepend a `MultiLocation` to a concrete asset, giving it a new root location.
|
||||
pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
|
||||
self.id.reanchor(prepend)
|
||||
}
|
||||
|
||||
/// Prepend a `MultiLocation` to a concrete asset, giving it a new root location.
|
||||
pub fn reanchored(mut self, prepend: &MultiLocation) -> Result<Self, ()> {
|
||||
self.reanchor(prepend)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Returns true if `self` is a super-set of the given `inner`.
|
||||
pub fn contains(&self, inner: &MultiAsset) -> bool {
|
||||
use Fungibility::*;
|
||||
if self.id == inner.id {
|
||||
match (&self.fun, &inner.fun) {
|
||||
(Fungible(a), Fungible(i)) if a >= i => return true,
|
||||
(NonFungible(a), NonFungible(i)) if a == i => return true,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<super::super::v0::MultiAsset> for MultiAsset {
|
||||
type Error = ();
|
||||
fn try_from(old: super::super::v0::MultiAsset) -> result::Result<MultiAsset, ()> {
|
||||
use super::super::v0::MultiAsset as V0;
|
||||
use AssetId::*;
|
||||
use Fungibility::*;
|
||||
let (id, fun) = match old {
|
||||
V0::ConcreteFungible { id, amount } => (Concrete(id.into()), Fungible(amount)),
|
||||
V0::ConcreteNonFungible { class, instance } =>
|
||||
(Concrete(class.into()), NonFungible(instance)),
|
||||
V0::AbstractFungible { id, amount } => (Abstract(id), Fungible(amount)),
|
||||
V0::AbstractNonFungible { class, instance } => (Abstract(class), NonFungible(instance)),
|
||||
_ => return Err(()),
|
||||
};
|
||||
Ok(MultiAsset { id, fun })
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<super::super::v0::MultiAsset> for Option<MultiAsset> {
|
||||
type Error = ();
|
||||
fn try_from(old: super::super::v0::MultiAsset) -> result::Result<Option<MultiAsset>, ()> {
|
||||
match old {
|
||||
super::super::v0::MultiAsset::None => return Ok(None),
|
||||
x => return Ok(Some(x.try_into()?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<super::super::v0::MultiAsset>> for MultiAsset {
|
||||
type Error = ();
|
||||
fn try_from(mut old: Vec<super::super::v0::MultiAsset>) -> result::Result<MultiAsset, ()> {
|
||||
if old.len() == 1 {
|
||||
old.remove(0).try_into()
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A `Vec` of `MultiAsset`s. There may be no duplicate fungible items in here and when decoding, they must be sorted.
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode)]
|
||||
pub struct MultiAssets(Vec<MultiAsset>);
|
||||
|
||||
impl Decode for MultiAssets {
|
||||
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, parity_scale_codec::Error> {
|
||||
Self::from_sorted_and_deduplicated(Vec::<MultiAsset>::decode(input)?)
|
||||
.map_err(|()| "Out of order".into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<super::super::v0::MultiAsset>> for MultiAssets {
|
||||
type Error = ();
|
||||
fn try_from(old: Vec<super::super::v0::MultiAsset>) -> result::Result<MultiAssets, ()> {
|
||||
let v = old
|
||||
.into_iter()
|
||||
.map(Option::<MultiAsset>::try_from)
|
||||
.filter_map(|x| x.transpose())
|
||||
.collect::<result::Result<Vec<MultiAsset>, ()>>()?;
|
||||
Ok(v.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<MultiAsset>> for MultiAssets {
|
||||
fn from(mut assets: Vec<MultiAsset>) -> Self {
|
||||
let mut res = Vec::with_capacity(assets.len());
|
||||
if !assets.is_empty() {
|
||||
assets.sort();
|
||||
let mut iter = assets.into_iter();
|
||||
if let Some(first) = iter.next() {
|
||||
let last = iter.fold(first, |a, b| -> MultiAsset {
|
||||
match (a, b) {
|
||||
(
|
||||
MultiAsset { fun: Fungibility::Fungible(a_amount), id: a_id },
|
||||
MultiAsset { fun: Fungibility::Fungible(b_amount), id: b_id },
|
||||
) if a_id == b_id =>
|
||||
MultiAsset { id: a_id, fun: Fungibility::Fungible(a_amount + b_amount) },
|
||||
(
|
||||
MultiAsset { fun: Fungibility::NonFungible(a_instance), id: a_id },
|
||||
MultiAsset { fun: Fungibility::NonFungible(b_instance), id: b_id },
|
||||
) if a_id == b_id && a_instance == b_instance =>
|
||||
MultiAsset { fun: Fungibility::NonFungible(a_instance), id: a_id },
|
||||
(to_push, to_remember) => {
|
||||
res.push(to_push);
|
||||
to_remember
|
||||
},
|
||||
}
|
||||
});
|
||||
res.push(last);
|
||||
}
|
||||
}
|
||||
Self(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<MultiAsset>> From<T> for MultiAssets {
|
||||
fn from(x: T) -> Self {
|
||||
Self(vec![x.into()])
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiAssets {
|
||||
/// A new (empty) value.
|
||||
pub fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
|
||||
/// Create a new instance of `MultiAssets` from a `Vec<MultiAsset>` whose contents are sorted and
|
||||
/// which contain no duplicates.
|
||||
///
|
||||
/// Returns `Ok` if the operation succeeds and `Err` if `r` is out of order or had duplicates. If you can't
|
||||
/// guarantee that `r` is sorted and deduplicated, then use `From::<Vec<MultiAsset>>::from` which is infallible.
|
||||
pub fn from_sorted_and_deduplicated(r: Vec<MultiAsset>) -> Result<Self, ()> {
|
||||
if r.is_empty() {
|
||||
return Ok(Self(Vec::new()))
|
||||
}
|
||||
r.iter().skip(1).try_fold(&r[0], |a, b| -> Result<&MultiAsset, ()> {
|
||||
if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) {
|
||||
Ok(b)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
})?;
|
||||
Ok(Self(r))
|
||||
}
|
||||
|
||||
/// Create a new instance of `MultiAssets` from a `Vec<MultiAsset>` whose contents are sorted and
|
||||
/// which contain no duplicates.
|
||||
///
|
||||
/// In release mode, this skips any checks to ensure that `r` is correct, making it a negligible-cost operation.
|
||||
/// Generally though you should avoid using it unless you have a strict proof that `r` is valid.
|
||||
#[cfg(test)]
|
||||
pub fn from_sorted_and_deduplicated_skip_checks(r: Vec<MultiAsset>) -> Self {
|
||||
Self::from_sorted_and_deduplicated(r).expect("Invalid input r is not sorted/deduped")
|
||||
}
|
||||
/// Create a new instance of `MultiAssets` from a `Vec<MultiAsset>` whose contents are sorted and
|
||||
/// which contain no duplicates.
|
||||
///
|
||||
/// In release mode, this skips any checks to ensure that `r` is correct, making it a negligible-cost operation.
|
||||
/// Generally though you should avoid using it unless you have a strict proof that `r` is valid.
|
||||
///
|
||||
/// In test mode, this checks anyway and panics on fail.
|
||||
#[cfg(not(test))]
|
||||
pub fn from_sorted_and_deduplicated_skip_checks(r: Vec<MultiAsset>) -> Self {
|
||||
Self(r)
|
||||
}
|
||||
|
||||
/// Add some asset onto the list, saturating. This is quite a laborious operation since it maintains the ordering.
|
||||
pub fn push(&mut self, a: MultiAsset) {
|
||||
if let Fungibility::Fungible(ref amount) = a.fun {
|
||||
for asset in self.0.iter_mut().filter(|x| x.id == a.id) {
|
||||
if let Fungibility::Fungible(ref mut balance) = asset.fun {
|
||||
*balance = balance.saturating_add(*amount);
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
self.0.push(a);
|
||||
self.0.sort();
|
||||
}
|
||||
|
||||
/// Returns `true` if this definitely represents no asset.
|
||||
pub fn is_none(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
/// Returns true if `self` is a super-set of the given `inner`.
|
||||
pub fn contains(&self, inner: &MultiAsset) -> bool {
|
||||
self.0.iter().any(|i| i.contains(inner))
|
||||
}
|
||||
|
||||
/// Consume `self` and return the inner vec.
|
||||
pub fn drain(self) -> Vec<MultiAsset> {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Return a reference to the inner vec.
|
||||
pub fn inner(&self) -> &Vec<MultiAsset> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Return the number of distinct asset instances contained.
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
/// Prepend a `MultiLocation` to any concrete asset items, giving it a new root location.
|
||||
pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
|
||||
self.0.iter_mut().try_for_each(|i| i.reanchor(prepend))
|
||||
}
|
||||
|
||||
/// Return a reference to an item at a specific index or `None` if it doesn't exist.
|
||||
pub fn get(&self, index: usize) -> Option<&MultiAsset> {
|
||||
self.0.get(index)
|
||||
}
|
||||
}
|
||||
/// Classification of whether an asset is fungible or not.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)]
|
||||
pub enum WildFungibility {
|
||||
Fungible,
|
||||
NonFungible,
|
||||
}
|
||||
|
||||
/// A wildcard representing a set of assets.
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)]
|
||||
pub enum WildMultiAsset {
|
||||
/// All assets in the holding register, up to `usize` individual assets (different instances of non-fungibles could
|
||||
/// be separate assets).
|
||||
All,
|
||||
/// All assets in the holding register of a given fungibility and ID. If operating on non-fungibles, then a limit
|
||||
/// is provided for the maximum amount of matching instances.
|
||||
AllOf { id: AssetId, fun: WildFungibility },
|
||||
}
|
||||
|
||||
impl TryFrom<super::super::v0::MultiAsset> for WildMultiAsset {
|
||||
type Error = ();
|
||||
fn try_from(old: super::super::v0::MultiAsset) -> result::Result<WildMultiAsset, ()> {
|
||||
use super::super::v0::MultiAsset as V0;
|
||||
use AssetId::*;
|
||||
use WildFungibility::*;
|
||||
let (id, fun) = match old {
|
||||
V0::All => return Ok(WildMultiAsset::All),
|
||||
V0::AllConcreteFungible { id } => (Concrete(id.into()), Fungible),
|
||||
V0::AllConcreteNonFungible { class } => (Concrete(class.into()), NonFungible),
|
||||
V0::AllAbstractFungible { id } => (Abstract(id), Fungible),
|
||||
V0::AllAbstractNonFungible { class } => (Abstract(class), NonFungible),
|
||||
_ => return Err(()),
|
||||
};
|
||||
Ok(WildMultiAsset::AllOf { id, fun })
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<super::super::v0::MultiAsset>> for WildMultiAsset {
|
||||
type Error = ();
|
||||
fn try_from(mut old: Vec<super::super::v0::MultiAsset>) -> result::Result<WildMultiAsset, ()> {
|
||||
if old.len() == 1 {
|
||||
old.remove(0).try_into()
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WildMultiAsset {
|
||||
/// Returns true if `self` is a super-set of the given `inner`.
|
||||
///
|
||||
/// Typically, any wildcard is never contained in anything else, and a wildcard can contain any other non-wildcard.
|
||||
/// For more details, see the implementation and tests.
|
||||
pub fn contains(&self, inner: &MultiAsset) -> bool {
|
||||
use WildMultiAsset::*;
|
||||
match self {
|
||||
AllOf { fun, id } => inner.fun.is_kind(*fun) && &inner.id == id,
|
||||
All => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepend a `MultiLocation` to any concrete asset components, giving it a new root location.
|
||||
pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
|
||||
use WildMultiAsset::*;
|
||||
match self {
|
||||
AllOf { ref mut id, .. } => id.reanchor(prepend).map_err(|_| ()),
|
||||
All => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Into<AssetId>, B: Into<WildFungibility>> From<(A, B)> for WildMultiAsset {
|
||||
fn from((id, fun): (A, B)) -> WildMultiAsset {
|
||||
WildMultiAsset::AllOf { fun: fun.into(), id: id.into() }
|
||||
}
|
||||
}
|
||||
|
||||
/// `MultiAsset` collection, either `MultiAssets` or a single wildcard.
|
||||
///
|
||||
/// Note: Vectors of wildcards whose encoding is supported in XCM v0 are unsupported
|
||||
/// in this implementation and will result in a decode error.
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)]
|
||||
pub enum MultiAssetFilter {
|
||||
Definite(MultiAssets),
|
||||
Wild(WildMultiAsset),
|
||||
}
|
||||
|
||||
impl<T: Into<WildMultiAsset>> From<T> for MultiAssetFilter {
|
||||
fn from(x: T) -> Self {
|
||||
Self::Wild(x.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultiAsset> for MultiAssetFilter {
|
||||
fn from(x: MultiAsset) -> Self {
|
||||
Self::Definite(vec![x].into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<MultiAsset>> for MultiAssetFilter {
|
||||
fn from(x: Vec<MultiAsset>) -> Self {
|
||||
Self::Definite(x.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultiAssets> for MultiAssetFilter {
|
||||
fn from(x: MultiAssets) -> Self {
|
||||
Self::Definite(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiAssetFilter {
|
||||
/// Returns true if `self` is a super-set of the given `inner`.
|
||||
///
|
||||
/// Typically, any wildcard is never contained in anything else, and a wildcard can contain any other non-wildcard.
|
||||
/// For more details, see the implementation and tests.
|
||||
pub fn contains(&self, inner: &MultiAsset) -> bool {
|
||||
match self {
|
||||
MultiAssetFilter::Definite(ref assets) => assets.contains(inner),
|
||||
MultiAssetFilter::Wild(ref wild) => wild.contains(inner),
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepend a `MultiLocation` to any concrete asset components, giving it a new root location.
|
||||
pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
|
||||
match self {
|
||||
MultiAssetFilter::Definite(ref mut assets) => assets.reanchor(prepend),
|
||||
MultiAssetFilter::Wild(ref mut wild) => wild.reanchor(prepend),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<super::super::v0::MultiAsset>> for MultiAssetFilter {
|
||||
type Error = ();
|
||||
fn try_from(
|
||||
mut old: Vec<super::super::v0::MultiAsset>,
|
||||
) -> result::Result<MultiAssetFilter, ()> {
|
||||
if old.len() == 1 && old[0].is_wildcard() {
|
||||
Ok(MultiAssetFilter::Wild(old.remove(0).try_into()?))
|
||||
} else {
|
||||
Ok(MultiAssetFilter::Definite(old.try_into()?))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,821 @@
|
||||
// Copyright 2020-2021 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Cumulus.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Cross-Consensus Message format data structures.
|
||||
|
||||
use super::{super::v0::MultiLocation as MultiLocation0, Junction};
|
||||
use core::{mem, result};
|
||||
use parity_scale_codec::{self, Decode, Encode};
|
||||
|
||||
/// A relative path between state-bearing consensus systems.
|
||||
///
|
||||
/// A location in a consensus system is defined as an *isolatable state machine* held within global consensus. The
|
||||
/// location in question need not have a sophisticated consensus algorithm of its own; a single account within
|
||||
/// Ethereum, for example, could be considered a location.
|
||||
///
|
||||
/// A very-much non-exhaustive list of types of location include:
|
||||
/// - A (normal, layer-1) block chain, e.g. the Bitcoin mainnet or a parachain.
|
||||
/// - A layer-0 super-chain, e.g. the Polkadot Relay chain.
|
||||
/// - A layer-2 smart contract, e.g. an ERC-20 on Ethereum.
|
||||
/// - A logical functional component of a chain, e.g. a single instance of a pallet on a Frame-based Substrate chain.
|
||||
/// - An account.
|
||||
///
|
||||
/// A `MultiLocation` is a *relative identifier*, meaning that it can only be used to define the relative path
|
||||
/// between two locations, and cannot generally be used to refer to a location universally. It is comprised of a
|
||||
/// number of *junctions*, each morphing the previous location, either diving down into one of its internal locations,
|
||||
/// called a *sub-consensus*, or going up into its parent location. Correct `MultiLocation` values must have all
|
||||
/// `Parent` junctions as a prefix to all *sub-consensus* junctions.
|
||||
///
|
||||
/// This specific `MultiLocation` implementation uses a Rust `enum` in order to make pattern matching easier.
|
||||
///
|
||||
/// The `MultiLocation` value of `Here` simply refers to the interpreting consensus system.
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)]
|
||||
pub enum MultiLocation {
|
||||
/// The interpreting consensus system.
|
||||
Here,
|
||||
/// A relative path comprising 1 junction.
|
||||
X1(Junction),
|
||||
/// A relative path comprising 2 junctions.
|
||||
X2(Junction, Junction),
|
||||
/// A relative path comprising 3 junctions.
|
||||
X3(Junction, Junction, Junction),
|
||||
/// A relative path comprising 4 junctions.
|
||||
X4(Junction, Junction, Junction, Junction),
|
||||
/// A relative path comprising 5 junctions.
|
||||
X5(Junction, Junction, Junction, Junction, Junction),
|
||||
/// A relative path comprising 6 junctions.
|
||||
X6(Junction, Junction, Junction, Junction, Junction, Junction),
|
||||
/// A relative path comprising 7 junctions.
|
||||
X7(Junction, Junction, Junction, Junction, Junction, Junction, Junction),
|
||||
/// A relative path comprising 8 junctions.
|
||||
X8(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction),
|
||||
}
|
||||
|
||||
/// Maximum number of junctions a `MultiLocation` can contain.
|
||||
pub const MAX_MULTILOCATION_LENGTH: usize = 8;
|
||||
|
||||
impl From<Junction> for MultiLocation {
|
||||
fn from(x: Junction) -> Self {
|
||||
MultiLocation::X1(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for MultiLocation {
|
||||
fn from(_: ()) -> Self {
|
||||
MultiLocation::Here
|
||||
}
|
||||
}
|
||||
impl From<(Junction,)> for MultiLocation {
|
||||
fn from(x: (Junction,)) -> Self {
|
||||
MultiLocation::X1(x.0)
|
||||
}
|
||||
}
|
||||
impl From<(Junction, Junction)> for MultiLocation {
|
||||
fn from(x: (Junction, Junction)) -> Self {
|
||||
MultiLocation::X2(x.0, x.1)
|
||||
}
|
||||
}
|
||||
impl From<(Junction, Junction, Junction)> for MultiLocation {
|
||||
fn from(x: (Junction, Junction, Junction)) -> Self {
|
||||
MultiLocation::X3(x.0, x.1, x.2)
|
||||
}
|
||||
}
|
||||
impl From<(Junction, Junction, Junction, Junction)> for MultiLocation {
|
||||
fn from(x: (Junction, Junction, Junction, Junction)) -> Self {
|
||||
MultiLocation::X4(x.0, x.1, x.2, x.3)
|
||||
}
|
||||
}
|
||||
impl From<(Junction, Junction, Junction, Junction, Junction)> for MultiLocation {
|
||||
fn from(x: (Junction, Junction, Junction, Junction, Junction)) -> Self {
|
||||
MultiLocation::X5(x.0, x.1, x.2, x.3, x.4)
|
||||
}
|
||||
}
|
||||
impl From<(Junction, Junction, Junction, Junction, Junction, Junction)> for MultiLocation {
|
||||
fn from(x: (Junction, Junction, Junction, Junction, Junction, Junction)) -> Self {
|
||||
MultiLocation::X6(x.0, x.1, x.2, x.3, x.4, x.5)
|
||||
}
|
||||
}
|
||||
impl From<(Junction, Junction, Junction, Junction, Junction, Junction, Junction)>
|
||||
for MultiLocation
|
||||
{
|
||||
fn from(x: (Junction, Junction, Junction, Junction, Junction, Junction, Junction)) -> Self {
|
||||
MultiLocation::X7(x.0, x.1, x.2, x.3, x.4, x.5, x.6)
|
||||
}
|
||||
}
|
||||
impl From<(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction)>
|
||||
for MultiLocation
|
||||
{
|
||||
fn from(
|
||||
x: (Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction),
|
||||
) -> Self {
|
||||
MultiLocation::X8(x.0, x.1, x.2, x.3, x.4, x.5, x.6, x.7)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[Junction; 0]> for MultiLocation {
|
||||
fn from(_: [Junction; 0]) -> Self {
|
||||
MultiLocation::Here
|
||||
}
|
||||
}
|
||||
impl From<[Junction; 1]> for MultiLocation {
|
||||
fn from(x: [Junction; 1]) -> Self {
|
||||
let [x0] = x;
|
||||
MultiLocation::X1(x0)
|
||||
}
|
||||
}
|
||||
impl From<[Junction; 2]> for MultiLocation {
|
||||
fn from(x: [Junction; 2]) -> Self {
|
||||
let [x0, x1] = x;
|
||||
MultiLocation::X2(x0, x1)
|
||||
}
|
||||
}
|
||||
impl From<[Junction; 3]> for MultiLocation {
|
||||
fn from(x: [Junction; 3]) -> Self {
|
||||
let [x0, x1, x2] = x;
|
||||
MultiLocation::X3(x0, x1, x2)
|
||||
}
|
||||
}
|
||||
impl From<[Junction; 4]> for MultiLocation {
|
||||
fn from(x: [Junction; 4]) -> Self {
|
||||
let [x0, x1, x2, x3] = x;
|
||||
MultiLocation::X4(x0, x1, x2, x3)
|
||||
}
|
||||
}
|
||||
impl From<[Junction; 5]> for MultiLocation {
|
||||
fn from(x: [Junction; 5]) -> Self {
|
||||
let [x0, x1, x2, x3, x4] = x;
|
||||
MultiLocation::X5(x0, x1, x2, x3, x4)
|
||||
}
|
||||
}
|
||||
impl From<[Junction; 6]> for MultiLocation {
|
||||
fn from(x: [Junction; 6]) -> Self {
|
||||
let [x0, x1, x2, x3, x4, x5] = x;
|
||||
MultiLocation::X6(x0, x1, x2, x3, x4, x5)
|
||||
}
|
||||
}
|
||||
impl From<[Junction; 7]> for MultiLocation {
|
||||
fn from(x: [Junction; 7]) -> Self {
|
||||
let [x0, x1, x2, x3, x4, x5, x6] = x;
|
||||
MultiLocation::X7(x0, x1, x2, x3, x4, x5, x6)
|
||||
}
|
||||
}
|
||||
impl From<[Junction; 8]> for MultiLocation {
|
||||
fn from(x: [Junction; 8]) -> Self {
|
||||
let [x0, x1, x2, x3, x4, x5, x6, x7] = x;
|
||||
MultiLocation::X8(x0, x1, x2, x3, x4, x5, x6, x7)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MultiLocationIterator(MultiLocation);
|
||||
impl Iterator for MultiLocationIterator {
|
||||
type Item = Junction;
|
||||
fn next(&mut self) -> Option<Junction> {
|
||||
self.0.take_first()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MultiLocationReverseIterator(MultiLocation);
|
||||
impl Iterator for MultiLocationReverseIterator {
|
||||
type Item = Junction;
|
||||
fn next(&mut self) -> Option<Junction> {
|
||||
self.0.take_last()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MultiLocationRefIterator<'a>(&'a MultiLocation, usize);
|
||||
impl<'a> Iterator for MultiLocationRefIterator<'a> {
|
||||
type Item = &'a Junction;
|
||||
fn next(&mut self) -> Option<&'a Junction> {
|
||||
let result = self.0.at(self.1);
|
||||
self.1 += 1;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MultiLocationReverseRefIterator<'a>(&'a MultiLocation, usize);
|
||||
impl<'a> Iterator for MultiLocationReverseRefIterator<'a> {
|
||||
type Item = &'a Junction;
|
||||
fn next(&mut self) -> Option<&'a Junction> {
|
||||
self.1 += 1;
|
||||
self.0.at(self.0.len().checked_sub(self.1)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiLocation {
|
||||
/// Returns first junction, or `None` if the location is empty.
|
||||
pub fn first(&self) -> Option<&Junction> {
|
||||
match &self {
|
||||
MultiLocation::Here => None,
|
||||
MultiLocation::X1(ref a) => Some(a),
|
||||
MultiLocation::X2(ref a, ..) => Some(a),
|
||||
MultiLocation::X3(ref a, ..) => Some(a),
|
||||
MultiLocation::X4(ref a, ..) => Some(a),
|
||||
MultiLocation::X5(ref a, ..) => Some(a),
|
||||
MultiLocation::X6(ref a, ..) => Some(a),
|
||||
MultiLocation::X7(ref a, ..) => Some(a),
|
||||
MultiLocation::X8(ref a, ..) => Some(a),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns last junction, or `None` if the location is empty.
|
||||
pub fn last(&self) -> Option<&Junction> {
|
||||
match &self {
|
||||
MultiLocation::Here => None,
|
||||
MultiLocation::X1(ref a) => Some(a),
|
||||
MultiLocation::X2(.., ref a) => Some(a),
|
||||
MultiLocation::X3(.., ref a) => Some(a),
|
||||
MultiLocation::X4(.., ref a) => Some(a),
|
||||
MultiLocation::X5(.., ref a) => Some(a),
|
||||
MultiLocation::X6(.., ref a) => Some(a),
|
||||
MultiLocation::X7(.., ref a) => Some(a),
|
||||
MultiLocation::X8(.., ref a) => Some(a),
|
||||
}
|
||||
}
|
||||
|
||||
/// Splits off the first junction, returning the remaining suffix (first item in tuple) and the first element
|
||||
/// (second item in tuple) or `None` if it was empty.
|
||||
pub fn split_first(self) -> (MultiLocation, Option<Junction>) {
|
||||
match self {
|
||||
MultiLocation::Here => (MultiLocation::Here, None),
|
||||
MultiLocation::X1(a) => (MultiLocation::Here, Some(a)),
|
||||
MultiLocation::X2(a, b) => (MultiLocation::X1(b), Some(a)),
|
||||
MultiLocation::X3(a, b, c) => (MultiLocation::X2(b, c), Some(a)),
|
||||
MultiLocation::X4(a, b, c, d) => (MultiLocation::X3(b, c, d), Some(a)),
|
||||
MultiLocation::X5(a, b, c, d, e) => (MultiLocation::X4(b, c, d, e), Some(a)),
|
||||
MultiLocation::X6(a, b, c, d, e, f) => (MultiLocation::X5(b, c, d, e, f), Some(a)),
|
||||
MultiLocation::X7(a, b, c, d, e, f, g) =>
|
||||
(MultiLocation::X6(b, c, d, e, f, g), Some(a)),
|
||||
MultiLocation::X8(a, b, c, d, e, f, g, h) =>
|
||||
(MultiLocation::X7(b, c, d, e, f, g, h), Some(a)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Splits off the last junction, returning the remaining prefix (first item in tuple) and the last element
|
||||
/// (second item in tuple) or `None` if it was empty.
|
||||
pub fn split_last(self) -> (MultiLocation, Option<Junction>) {
|
||||
match self {
|
||||
MultiLocation::Here => (MultiLocation::Here, None),
|
||||
MultiLocation::X1(a) => (MultiLocation::Here, Some(a)),
|
||||
MultiLocation::X2(a, b) => (MultiLocation::X1(a), Some(b)),
|
||||
MultiLocation::X3(a, b, c) => (MultiLocation::X2(a, b), Some(c)),
|
||||
MultiLocation::X4(a, b, c, d) => (MultiLocation::X3(a, b, c), Some(d)),
|
||||
MultiLocation::X5(a, b, c, d, e) => (MultiLocation::X4(a, b, c, d), Some(e)),
|
||||
MultiLocation::X6(a, b, c, d, e, f) => (MultiLocation::X5(a, b, c, d, e), Some(f)),
|
||||
MultiLocation::X7(a, b, c, d, e, f, g) =>
|
||||
(MultiLocation::X6(a, b, c, d, e, f), Some(g)),
|
||||
MultiLocation::X8(a, b, c, d, e, f, g, h) =>
|
||||
(MultiLocation::X7(a, b, c, d, e, f, g), Some(h)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the first element from `self`, returning it (or `None` if it was empty).
|
||||
pub fn take_first(&mut self) -> Option<Junction> {
|
||||
let mut d = MultiLocation::Here;
|
||||
mem::swap(&mut *self, &mut d);
|
||||
let (tail, head) = d.split_first();
|
||||
*self = tail;
|
||||
head
|
||||
}
|
||||
|
||||
/// Removes the last element from `self`, returning it (or `None` if it was empty).
|
||||
pub fn take_last(&mut self) -> Option<Junction> {
|
||||
let mut d = MultiLocation::Here;
|
||||
mem::swap(&mut *self, &mut d);
|
||||
let (head, tail) = d.split_last();
|
||||
*self = head;
|
||||
tail
|
||||
}
|
||||
|
||||
/// Consumes `self` and returns a `MultiLocation` suffixed with `new`, or an `Err` with the original value of
|
||||
/// `self` in case of overflow.
|
||||
pub fn pushed_with(self, new: Junction) -> result::Result<Self, Self> {
|
||||
Ok(match self {
|
||||
MultiLocation::Here => MultiLocation::X1(new),
|
||||
MultiLocation::X1(a) => MultiLocation::X2(a, new),
|
||||
MultiLocation::X2(a, b) => MultiLocation::X3(a, b, new),
|
||||
MultiLocation::X3(a, b, c) => MultiLocation::X4(a, b, c, new),
|
||||
MultiLocation::X4(a, b, c, d) => MultiLocation::X5(a, b, c, d, new),
|
||||
MultiLocation::X5(a, b, c, d, e) => MultiLocation::X6(a, b, c, d, e, new),
|
||||
MultiLocation::X6(a, b, c, d, e, f) => MultiLocation::X7(a, b, c, d, e, f, new),
|
||||
MultiLocation::X7(a, b, c, d, e, f, g) => MultiLocation::X8(a, b, c, d, e, f, g, new),
|
||||
s => Err(s)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Consumes `self` and returns a `MultiLocation` prefixed with `new`, or an `Err` with the original value of
|
||||
/// `self` in case of overflow.
|
||||
pub fn pushed_front_with(self, new: Junction) -> result::Result<Self, Self> {
|
||||
Ok(match self {
|
||||
MultiLocation::Here => MultiLocation::X1(new),
|
||||
MultiLocation::X1(a) => MultiLocation::X2(new, a),
|
||||
MultiLocation::X2(a, b) => MultiLocation::X3(new, a, b),
|
||||
MultiLocation::X3(a, b, c) => MultiLocation::X4(new, a, b, c),
|
||||
MultiLocation::X4(a, b, c, d) => MultiLocation::X5(new, a, b, c, d),
|
||||
MultiLocation::X5(a, b, c, d, e) => MultiLocation::X6(new, a, b, c, d, e),
|
||||
MultiLocation::X6(a, b, c, d, e, f) => MultiLocation::X7(new, a, b, c, d, e, f),
|
||||
MultiLocation::X7(a, b, c, d, e, f, g) => MultiLocation::X8(new, a, b, c, d, e, f, g),
|
||||
s => Err(s)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the number of junctions in `self`.
|
||||
pub fn len(&self) -> usize {
|
||||
match &self {
|
||||
MultiLocation::Here => 0,
|
||||
MultiLocation::X1(..) => 1,
|
||||
MultiLocation::X2(..) => 2,
|
||||
MultiLocation::X3(..) => 3,
|
||||
MultiLocation::X4(..) => 4,
|
||||
MultiLocation::X5(..) => 5,
|
||||
MultiLocation::X6(..) => 6,
|
||||
MultiLocation::X7(..) => 7,
|
||||
MultiLocation::X8(..) => 8,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the junction at index `i`, or `None` if the location doesn't contain that many elements.
|
||||
pub fn at(&self, i: usize) -> Option<&Junction> {
|
||||
Some(match (i, &self) {
|
||||
(0, MultiLocation::X1(ref a)) => a,
|
||||
(0, MultiLocation::X2(ref a, ..)) => a,
|
||||
(0, MultiLocation::X3(ref a, ..)) => a,
|
||||
(0, MultiLocation::X4(ref a, ..)) => a,
|
||||
(0, MultiLocation::X5(ref a, ..)) => a,
|
||||
(0, MultiLocation::X6(ref a, ..)) => a,
|
||||
(0, MultiLocation::X7(ref a, ..)) => a,
|
||||
(0, MultiLocation::X8(ref a, ..)) => a,
|
||||
(1, MultiLocation::X2(_, ref a)) => a,
|
||||
(1, MultiLocation::X3(_, ref a, ..)) => a,
|
||||
(1, MultiLocation::X4(_, ref a, ..)) => a,
|
||||
(1, MultiLocation::X5(_, ref a, ..)) => a,
|
||||
(1, MultiLocation::X6(_, ref a, ..)) => a,
|
||||
(1, MultiLocation::X7(_, ref a, ..)) => a,
|
||||
(1, MultiLocation::X8(_, ref a, ..)) => a,
|
||||
(2, MultiLocation::X3(_, _, ref a)) => a,
|
||||
(2, MultiLocation::X4(_, _, ref a, ..)) => a,
|
||||
(2, MultiLocation::X5(_, _, ref a, ..)) => a,
|
||||
(2, MultiLocation::X6(_, _, ref a, ..)) => a,
|
||||
(2, MultiLocation::X7(_, _, ref a, ..)) => a,
|
||||
(2, MultiLocation::X8(_, _, ref a, ..)) => a,
|
||||
(3, MultiLocation::X4(_, _, _, ref a)) => a,
|
||||
(3, MultiLocation::X5(_, _, _, ref a, ..)) => a,
|
||||
(3, MultiLocation::X6(_, _, _, ref a, ..)) => a,
|
||||
(3, MultiLocation::X7(_, _, _, ref a, ..)) => a,
|
||||
(3, MultiLocation::X8(_, _, _, ref a, ..)) => a,
|
||||
(4, MultiLocation::X5(_, _, _, _, ref a)) => a,
|
||||
(4, MultiLocation::X6(_, _, _, _, ref a, ..)) => a,
|
||||
(4, MultiLocation::X7(_, _, _, _, ref a, ..)) => a,
|
||||
(4, MultiLocation::X8(_, _, _, _, ref a, ..)) => a,
|
||||
(5, MultiLocation::X6(_, _, _, _, _, ref a)) => a,
|
||||
(5, MultiLocation::X7(_, _, _, _, _, ref a, ..)) => a,
|
||||
(5, MultiLocation::X8(_, _, _, _, _, ref a, ..)) => a,
|
||||
(6, MultiLocation::X7(_, _, _, _, _, _, ref a)) => a,
|
||||
(6, MultiLocation::X8(_, _, _, _, _, _, ref a, ..)) => a,
|
||||
(7, MultiLocation::X8(_, _, _, _, _, _, _, ref a)) => a,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the junction at index `i`, or `None` if the location doesn't contain that many
|
||||
/// elements.
|
||||
pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> {
|
||||
Some(match (i, self) {
|
||||
(0, MultiLocation::X1(ref mut a)) => a,
|
||||
(0, MultiLocation::X2(ref mut a, ..)) => a,
|
||||
(0, MultiLocation::X3(ref mut a, ..)) => a,
|
||||
(0, MultiLocation::X4(ref mut a, ..)) => a,
|
||||
(0, MultiLocation::X5(ref mut a, ..)) => a,
|
||||
(0, MultiLocation::X6(ref mut a, ..)) => a,
|
||||
(0, MultiLocation::X7(ref mut a, ..)) => a,
|
||||
(0, MultiLocation::X8(ref mut a, ..)) => a,
|
||||
(1, MultiLocation::X2(_, ref mut a)) => a,
|
||||
(1, MultiLocation::X3(_, ref mut a, ..)) => a,
|
||||
(1, MultiLocation::X4(_, ref mut a, ..)) => a,
|
||||
(1, MultiLocation::X5(_, ref mut a, ..)) => a,
|
||||
(1, MultiLocation::X6(_, ref mut a, ..)) => a,
|
||||
(1, MultiLocation::X7(_, ref mut a, ..)) => a,
|
||||
(1, MultiLocation::X8(_, ref mut a, ..)) => a,
|
||||
(2, MultiLocation::X3(_, _, ref mut a)) => a,
|
||||
(2, MultiLocation::X4(_, _, ref mut a, ..)) => a,
|
||||
(2, MultiLocation::X5(_, _, ref mut a, ..)) => a,
|
||||
(2, MultiLocation::X6(_, _, ref mut a, ..)) => a,
|
||||
(2, MultiLocation::X7(_, _, ref mut a, ..)) => a,
|
||||
(2, MultiLocation::X8(_, _, ref mut a, ..)) => a,
|
||||
(3, MultiLocation::X4(_, _, _, ref mut a)) => a,
|
||||
(3, MultiLocation::X5(_, _, _, ref mut a, ..)) => a,
|
||||
(3, MultiLocation::X6(_, _, _, ref mut a, ..)) => a,
|
||||
(3, MultiLocation::X7(_, _, _, ref mut a, ..)) => a,
|
||||
(3, MultiLocation::X8(_, _, _, ref mut a, ..)) => a,
|
||||
(4, MultiLocation::X5(_, _, _, _, ref mut a)) => a,
|
||||
(4, MultiLocation::X6(_, _, _, _, ref mut a, ..)) => a,
|
||||
(4, MultiLocation::X7(_, _, _, _, ref mut a, ..)) => a,
|
||||
(4, MultiLocation::X8(_, _, _, _, ref mut a, ..)) => a,
|
||||
(5, MultiLocation::X6(_, _, _, _, _, ref mut a)) => a,
|
||||
(5, MultiLocation::X7(_, _, _, _, _, ref mut a, ..)) => a,
|
||||
(5, MultiLocation::X8(_, _, _, _, _, ref mut a, ..)) => a,
|
||||
(6, MultiLocation::X7(_, _, _, _, _, _, ref mut a)) => a,
|
||||
(6, MultiLocation::X8(_, _, _, _, _, _, ref mut a, ..)) => a,
|
||||
(7, MultiLocation::X8(_, _, _, _, _, _, _, ref mut a)) => a,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a reference iterator over the junctions.
|
||||
pub fn iter(&self) -> MultiLocationRefIterator {
|
||||
MultiLocationRefIterator(&self, 0)
|
||||
}
|
||||
|
||||
/// Returns a reference iterator over the junctions in reverse.
|
||||
pub fn iter_rev(&self) -> MultiLocationReverseRefIterator {
|
||||
MultiLocationReverseRefIterator(&self, 0)
|
||||
}
|
||||
|
||||
/// Consumes `self` and returns an iterator over the junctions.
|
||||
pub fn into_iter(self) -> MultiLocationIterator {
|
||||
MultiLocationIterator(self)
|
||||
}
|
||||
|
||||
/// Consumes `self` and returns an iterator over the junctions in reverse.
|
||||
pub fn into_iter_rev(self) -> MultiLocationReverseIterator {
|
||||
MultiLocationReverseIterator(self)
|
||||
}
|
||||
|
||||
/// Ensures that self begins with `prefix` and that it has a single `Junction` item following.
|
||||
/// If so, returns a reference to this `Junction` item.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// # use xcm::latest::{MultiLocation::*, Junction::*};
|
||||
/// # fn main() {
|
||||
/// let mut m = X3(Parent, PalletInstance(3), OnlyChild);
|
||||
/// assert_eq!(m.match_and_split(&X2(Parent, PalletInstance(3))), Some(&OnlyChild));
|
||||
/// assert_eq!(m.match_and_split(&X1(Parent)), None);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn match_and_split(&self, prefix: &MultiLocation) -> Option<&Junction> {
|
||||
if prefix.len() + 1 != self.len() {
|
||||
return None
|
||||
}
|
||||
for i in 0..prefix.len() {
|
||||
if prefix.at(i) != self.at(i) {
|
||||
return None
|
||||
}
|
||||
}
|
||||
return self.at(prefix.len())
|
||||
}
|
||||
|
||||
/// Mutates `self`, suffixing it with `new`. Returns `Err` in case of overflow.
|
||||
pub fn push(&mut self, new: Junction) -> result::Result<(), ()> {
|
||||
let mut n = MultiLocation::Here;
|
||||
mem::swap(&mut *self, &mut n);
|
||||
match n.pushed_with(new) {
|
||||
Ok(result) => {
|
||||
*self = result;
|
||||
Ok(())
|
||||
},
|
||||
Err(old) => {
|
||||
*self = old;
|
||||
Err(())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutates `self`, prefixing it with `new`. Returns `Err` in case of overflow.
|
||||
pub fn push_front(&mut self, new: Junction) -> result::Result<(), ()> {
|
||||
let mut n = MultiLocation::Here;
|
||||
mem::swap(&mut *self, &mut n);
|
||||
match n.pushed_front_with(new) {
|
||||
Ok(result) => {
|
||||
*self = result;
|
||||
Ok(())
|
||||
},
|
||||
Err(old) => {
|
||||
*self = old;
|
||||
Err(())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of `Parent` junctions at the beginning of `self`.
|
||||
pub fn leading_parent_count(&self) -> usize {
|
||||
use Junction::Parent;
|
||||
match self {
|
||||
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent) => 8,
|
||||
|
||||
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, ..) => 7,
|
||||
MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent) => 7,
|
||||
|
||||
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, ..) => 6,
|
||||
MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, Parent, ..) => 6,
|
||||
MultiLocation::X6(Parent, Parent, Parent, Parent, Parent, Parent) => 6,
|
||||
|
||||
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, ..) => 5,
|
||||
MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, ..) => 5,
|
||||
MultiLocation::X6(Parent, Parent, Parent, Parent, Parent, ..) => 5,
|
||||
MultiLocation::X5(Parent, Parent, Parent, Parent, Parent) => 5,
|
||||
|
||||
MultiLocation::X8(Parent, Parent, Parent, Parent, ..) => 4,
|
||||
MultiLocation::X7(Parent, Parent, Parent, Parent, ..) => 4,
|
||||
MultiLocation::X6(Parent, Parent, Parent, Parent, ..) => 4,
|
||||
MultiLocation::X5(Parent, Parent, Parent, Parent, ..) => 4,
|
||||
MultiLocation::X4(Parent, Parent, Parent, Parent) => 4,
|
||||
|
||||
MultiLocation::X8(Parent, Parent, Parent, ..) => 3,
|
||||
MultiLocation::X7(Parent, Parent, Parent, ..) => 3,
|
||||
MultiLocation::X6(Parent, Parent, Parent, ..) => 3,
|
||||
MultiLocation::X5(Parent, Parent, Parent, ..) => 3,
|
||||
MultiLocation::X4(Parent, Parent, Parent, ..) => 3,
|
||||
MultiLocation::X3(Parent, Parent, Parent) => 3,
|
||||
|
||||
MultiLocation::X8(Parent, Parent, ..) => 2,
|
||||
MultiLocation::X7(Parent, Parent, ..) => 2,
|
||||
MultiLocation::X6(Parent, Parent, ..) => 2,
|
||||
MultiLocation::X5(Parent, Parent, ..) => 2,
|
||||
MultiLocation::X4(Parent, Parent, ..) => 2,
|
||||
MultiLocation::X3(Parent, Parent, ..) => 2,
|
||||
MultiLocation::X2(Parent, Parent) => 2,
|
||||
|
||||
MultiLocation::X8(Parent, ..) => 1,
|
||||
MultiLocation::X7(Parent, ..) => 1,
|
||||
MultiLocation::X6(Parent, ..) => 1,
|
||||
MultiLocation::X5(Parent, ..) => 1,
|
||||
MultiLocation::X4(Parent, ..) => 1,
|
||||
MultiLocation::X3(Parent, ..) => 1,
|
||||
MultiLocation::X2(Parent, ..) => 1,
|
||||
MultiLocation::X1(Parent) => 1,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// This function ensures a multi-junction is in its canonicalized/normalized form, removing
|
||||
/// any internal `[Non-Parent, Parent]` combinations.
|
||||
pub fn canonicalize(&mut self) {
|
||||
let mut normalized = MultiLocation::Here;
|
||||
let mut iter = self.iter();
|
||||
// We build up the the new normalized path by taking items from the original multi-location.
|
||||
// When the next item we would add is `Parent`, we instead remove the last item assuming
|
||||
// it is non-parent.
|
||||
const EXPECT_MESSAGE: &'static str =
|
||||
"`self` is a well formed multi-location with N junctions; \
|
||||
this loop iterates over the junctions of `self`; \
|
||||
the loop can push to the new multi-location at most one time; \
|
||||
thus the size of the new multi-location is at most N junctions; \
|
||||
qed";
|
||||
while let Some(j) = iter.next() {
|
||||
if j == &Junction::Parent {
|
||||
match normalized.last() {
|
||||
None | Some(Junction::Parent) => {},
|
||||
Some(_) => {
|
||||
normalized.take_last();
|
||||
continue
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
normalized.push(j.clone()).expect(EXPECT_MESSAGE);
|
||||
}
|
||||
|
||||
core::mem::swap(self, &mut normalized);
|
||||
}
|
||||
|
||||
/// Mutate `self` so that it is suffixed with `suffix`. The correct normalized form is returned,
|
||||
/// removing any internal `[Non-Parent, Parent]` combinations.
|
||||
///
|
||||
/// In the case of overflow, `self` is unmodified and we return `Err` with `suffix`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// # use xcm::latest::{MultiLocation::*, Junction::*};
|
||||
/// # fn main() {
|
||||
/// let mut m = X3(Parent, Parachain(21), OnlyChild);
|
||||
/// assert_eq!(m.append_with(X2(Parent, PalletInstance(3))), Ok(()));
|
||||
/// assert_eq!(m, X3(Parent, Parachain(21), PalletInstance(3)));
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn append_with(&mut self, suffix: MultiLocation) -> Result<(), MultiLocation> {
|
||||
let mut prefix = suffix;
|
||||
core::mem::swap(self, &mut prefix);
|
||||
match self.prepend_with(prefix) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(prefix) => {
|
||||
let mut suffix = prefix;
|
||||
core::mem::swap(self, &mut suffix);
|
||||
Err(suffix)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutate `self` so that it is prefixed with `prefix`. The correct normalized form is returned,
|
||||
/// removing any internal [Non-Parent, `Parent`] combinations.
|
||||
///
|
||||
/// In the case of overflow, `self` is unmodified and we return `Err` with `prefix`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// # use xcm::latest::{MultiLocation::*, Junction::*, NetworkId::Any};
|
||||
/// # fn main() {
|
||||
/// let mut m = X3(Parent, Parent, PalletInstance(3));
|
||||
/// assert_eq!(m.prepend_with(X3(Parent, Parachain(21), OnlyChild)), Ok(()));
|
||||
/// assert_eq!(m, X2(Parent, PalletInstance(3)));
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn prepend_with(&mut self, prefix: MultiLocation) -> Result<(), MultiLocation> {
|
||||
let mut prefix = prefix;
|
||||
|
||||
// This will guarantee that all `Parent` junctions in the prefix are leading, which is
|
||||
// important for calculating the `skipped` items below.
|
||||
prefix.canonicalize();
|
||||
|
||||
let self_leading_parents = self.leading_parent_count();
|
||||
// These are the number of `non-parent` items in the prefix that we can
|
||||
// potentially remove if the original location leads with parents.
|
||||
let prefix_rest = prefix.len() - prefix.leading_parent_count();
|
||||
// 2 * skipped items will be removed when performing the normalization below.
|
||||
let skipped = self_leading_parents.min(prefix_rest);
|
||||
|
||||
// Pre-pending this prefix would create a multi-location with too many junctions.
|
||||
if self.len() + prefix.len() - 2 * skipped > MAX_MULTILOCATION_LENGTH {
|
||||
return Err(prefix)
|
||||
}
|
||||
|
||||
// Here we cancel out `[Non-Parent, Parent]` items (normalization), where
|
||||
// the non-parent item comes from the end of the prefix, and the parent item
|
||||
// comes from the front of the original location.
|
||||
//
|
||||
// We calculated already how many of these there should be above.
|
||||
for _ in 0..skipped {
|
||||
let _non_parent = prefix.take_last();
|
||||
let _parent = self.take_first();
|
||||
debug_assert!(
|
||||
_non_parent.is_some() && _non_parent != Some(Junction::Parent),
|
||||
"prepend_with should always remove a non-parent from the end of the prefix",
|
||||
);
|
||||
debug_assert!(
|
||||
_parent == Some(Junction::Parent),
|
||||
"prepend_with should always remove a parent from the front of the location",
|
||||
);
|
||||
}
|
||||
|
||||
for j in prefix.into_iter_rev() {
|
||||
self.push_front(j)
|
||||
.expect("len + prefix minus 2*skipped is less than max length; qed");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns true iff `self` is an interior location. For this it may not contain any `Junction`s
|
||||
/// for which `Junction::is_interior` returns `false`. This is generally true, except for the
|
||||
/// `Parent` item.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// # use xcm::latest::{MultiLocation::*, Junction::*, NetworkId::Any};
|
||||
/// # fn main() {
|
||||
/// let parent = X1(Parent);
|
||||
/// assert_eq!(parent.is_interior(), false);
|
||||
/// let m = X2(PalletInstance(12), AccountIndex64 { network: Any, index: 23 });
|
||||
/// assert_eq!(m.is_interior(), true);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn is_interior(&self) -> bool {
|
||||
self.iter().all(Junction::is_interior)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultiLocation0> for MultiLocation {
|
||||
fn from(old: MultiLocation0) -> Self {
|
||||
use MultiLocation::*;
|
||||
match old {
|
||||
MultiLocation0::Null => Here,
|
||||
MultiLocation0::X1(j0) => X1(j0),
|
||||
MultiLocation0::X2(j0, j1) => X2(j0, j1),
|
||||
MultiLocation0::X3(j0, j1, j2) => X3(j0, j1, j2),
|
||||
MultiLocation0::X4(j0, j1, j2, j3) => X4(j0, j1, j2, j3),
|
||||
MultiLocation0::X5(j0, j1, j2, j3, j4) => X5(j0, j1, j2, j3, j4),
|
||||
MultiLocation0::X6(j0, j1, j2, j3, j4, j5) => X6(j0, j1, j2, j3, j4, j5),
|
||||
MultiLocation0::X7(j0, j1, j2, j3, j4, j5, j6) => X7(j0, j1, j2, j3, j4, j5, j6),
|
||||
MultiLocation0::X8(j0, j1, j2, j3, j4, j5, j6, j7) =>
|
||||
X8(j0, j1, j2, j3, j4, j5, j6, j7),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::MultiLocation::*;
|
||||
use crate::opaque::v1::{Junction::*, NetworkId::Any};
|
||||
|
||||
#[test]
|
||||
fn match_and_split_works() {
|
||||
let m = X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 });
|
||||
assert_eq!(m.match_and_split(&X1(Parent)), None);
|
||||
assert_eq!(
|
||||
m.match_and_split(&X2(Parent, Parachain(42))),
|
||||
Some(&AccountIndex64 { network: Any, index: 23 })
|
||||
);
|
||||
assert_eq!(m.match_and_split(&m), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn append_with_works() {
|
||||
let acc = AccountIndex64 { network: Any, index: 23 };
|
||||
let mut m = X2(Parent, Parachain(42));
|
||||
assert_eq!(m.append_with(X2(PalletInstance(3), acc.clone())), Ok(()));
|
||||
assert_eq!(m, X4(Parent, Parachain(42), PalletInstance(3), acc.clone()));
|
||||
|
||||
// cannot append to create overly long multilocation
|
||||
let acc = AccountIndex64 { network: Any, index: 23 };
|
||||
let mut m = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parachain(42));
|
||||
let suffix = X2(PalletInstance(3), acc.clone());
|
||||
assert_eq!(m.append_with(suffix.clone()), Err(suffix));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepend_with_works() {
|
||||
let mut m = X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 });
|
||||
assert_eq!(m.prepend_with(X2(Parent, OnlyChild)), Ok(()));
|
||||
assert_eq!(m, X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 }));
|
||||
|
||||
// cannot prepend to create overly long multilocation
|
||||
let mut m = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parachain(42));
|
||||
let prefix = X2(Parent, Parent);
|
||||
assert_eq!(m.prepend_with(prefix.clone()), Err(prefix));
|
||||
|
||||
// Can handle shared prefix and resizing correctly.
|
||||
let mut m = X1(Parent);
|
||||
let prefix = X8(
|
||||
Parachain(100),
|
||||
OnlyChild,
|
||||
OnlyChild,
|
||||
OnlyChild,
|
||||
OnlyChild,
|
||||
OnlyChild,
|
||||
OnlyChild,
|
||||
Parent,
|
||||
);
|
||||
assert_eq!(m.prepend_with(prefix.clone()), Ok(()));
|
||||
assert_eq!(m, X5(Parachain(100), OnlyChild, OnlyChild, OnlyChild, OnlyChild));
|
||||
|
||||
let mut m = X1(Parent);
|
||||
let prefix = X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent);
|
||||
assert_eq!(m.prepend_with(prefix.clone()), Err(prefix));
|
||||
|
||||
let mut m = X1(Parent);
|
||||
let prefix = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent);
|
||||
assert_eq!(m.prepend_with(prefix.clone()), Ok(()));
|
||||
assert_eq!(m, X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent));
|
||||
|
||||
let mut m = X1(Parent);
|
||||
let prefix = X8(Parent, Parent, Parent, Parent, OnlyChild, Parent, Parent, Parent);
|
||||
assert_eq!(m.prepend_with(prefix.clone()), Ok(()));
|
||||
assert_eq!(m, X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn canonicalize_works() {
|
||||
let mut m = X1(Parent);
|
||||
m.canonicalize();
|
||||
assert_eq!(m, X1(Parent));
|
||||
|
||||
let mut m = X1(Parachain(1));
|
||||
m.canonicalize();
|
||||
assert_eq!(m, X1(Parachain(1)));
|
||||
|
||||
let mut m = X6(Parent, Parachain(1), Parent, Parachain(2), Parent, Parachain(3));
|
||||
m.canonicalize();
|
||||
assert_eq!(m, X2(Parent, Parachain(3)));
|
||||
|
||||
let mut m = X5(Parachain(1), Parent, Parachain(2), Parent, Parachain(3));
|
||||
m.canonicalize();
|
||||
assert_eq!(m, X1(Parachain(3)));
|
||||
|
||||
let mut m = X6(Parachain(1), Parent, Parachain(2), Parent, Parachain(3), Parent);
|
||||
m.canonicalize();
|
||||
assert_eq!(m, Here);
|
||||
|
||||
let mut m = X5(Parachain(1), Parent, Parent, Parent, Parachain(3));
|
||||
m.canonicalize();
|
||||
assert_eq!(m, X3(Parent, Parent, Parachain(3)));
|
||||
|
||||
let mut m = X4(Parachain(1), Parachain(2), Parent, Parent);
|
||||
m.canonicalize();
|
||||
assert_eq!(m, Here);
|
||||
|
||||
let mut m = X4(Parent, Parent, Parachain(1), Parachain(2));
|
||||
m.canonicalize();
|
||||
assert_eq!(m, X4(Parent, Parent, Parachain(1), Parachain(2)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Cumulus.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Version 1 of the Cross-Consensus Message format data structures.
|
||||
|
||||
use super::{
|
||||
super::v0::Order as Order0, MultiAsset, MultiAssetFilter, MultiAssets, MultiLocation, Xcm,
|
||||
};
|
||||
use alloc::{vec, vec::Vec};
|
||||
use core::{
|
||||
convert::{TryFrom, TryInto},
|
||||
result,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use parity_scale_codec::{self, Decode, Encode};
|
||||
|
||||
/// An instruction to be executed on some or all of the assets in holding, used by asset-related XCM messages.
|
||||
#[derive(Derivative, Encode, Decode)]
|
||||
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
|
||||
#[codec(encode_bound())]
|
||||
#[codec(decode_bound())]
|
||||
pub enum Order<Call> {
|
||||
/// Do nothing. Not generally used.
|
||||
#[codec(index = 0)]
|
||||
Noop,
|
||||
|
||||
/// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `beneficiary`
|
||||
/// within this consensus system.
|
||||
///
|
||||
/// - `assets`: The asset(s) to remove from holding.
|
||||
/// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. Only the first
|
||||
/// `max_assets` assets/instances of those matched by `assets` will be removed, prioritized under standard asset
|
||||
/// ordering. Any others will remain in holding.
|
||||
/// - `beneficiary`: The new owner for the assets.
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 1)]
|
||||
DepositAsset { assets: MultiAssetFilter, max_assets: u32, beneficiary: MultiLocation },
|
||||
|
||||
/// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within
|
||||
/// this consensus system (i.e. its sovereign account).
|
||||
///
|
||||
/// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`.
|
||||
///
|
||||
/// - `assets`: The asset(s) to remove from holding.
|
||||
/// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. Only the first
|
||||
/// `max_assets` assets/instances of those matched by `assets` will be removed, prioritized under standard asset
|
||||
/// ordering. Any others will remain in holding.
|
||||
/// - `dest`: The location whose sovereign account will own the assets and thus the effective beneficiary for the
|
||||
/// assets and the notification target for the reserve asset deposit message.
|
||||
/// - `effects`: The orders that should be contained in the `ReserveAssetDeposited` which is sent onwards to
|
||||
/// `dest`.
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 2)]
|
||||
DepositReserveAsset {
|
||||
assets: MultiAssetFilter,
|
||||
max_assets: u32,
|
||||
dest: MultiLocation,
|
||||
effects: Vec<Order<()>>,
|
||||
},
|
||||
|
||||
/// Remove the asset(s) (`give`) from holding and replace them with alternative assets.
|
||||
///
|
||||
/// The minimum amount of assets to be received into holding for the order not to fail may be stated.
|
||||
///
|
||||
/// - `give`: The asset(s) to remove from holding.
|
||||
/// - `receive`: The minimum amount of assets(s) which `give` should be exchanged for.
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 3)]
|
||||
ExchangeAsset { give: MultiAssetFilter, receive: MultiAssets },
|
||||
|
||||
/// Remove the asset(s) (`assets`) from holding and send a `WithdrawAsset` XCM message to a reserve location.
|
||||
///
|
||||
/// - `assets`: The asset(s) to remove from holding.
|
||||
/// - `reserve`: A valid location that acts as a reserve for all asset(s) in `assets`. The sovereign account
|
||||
/// of this consensus system *on the reserve location* will have appropriate assets withdrawn and `effects` will
|
||||
/// be executed on them. There will typically be only one valid location on any given asset/chain combination.
|
||||
/// - `effects`: The orders to execute on the assets once withdrawn *on the reserve location*.
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 4)]
|
||||
InitiateReserveWithdraw {
|
||||
assets: MultiAssetFilter,
|
||||
reserve: MultiLocation,
|
||||
effects: Vec<Order<()>>,
|
||||
},
|
||||
|
||||
/// Remove the asset(s) (`assets`) from holding and send a `ReceiveTeleportedAsset` XCM message to a `destination`
|
||||
/// location.
|
||||
///
|
||||
/// - `assets`: The asset(s) to remove from holding.
|
||||
/// - `destination`: A valid location that has a bi-lateral teleportation arrangement.
|
||||
/// - `effects`: The orders to execute on the assets once arrived *on the destination location*.
|
||||
///
|
||||
/// NOTE: The `destination` location *MUST* respect this origin as a valid teleportation origin for all `assets`.
|
||||
/// If it does not, then the assets may be lost.
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 5)]
|
||||
InitiateTeleport { assets: MultiAssetFilter, dest: MultiLocation, effects: Vec<Order<()>> },
|
||||
|
||||
/// Send a `Balances` XCM message with the `assets` value equal to the holding contents, or a portion thereof.
|
||||
///
|
||||
/// - `query_id`: An identifier that will be replicated into the returned XCM message.
|
||||
/// - `dest`: A valid destination for the returned XCM message. This may be limited to the current origin.
|
||||
/// - `assets`: A filter for the assets that should be reported back. The assets reported back will be, asset-
|
||||
/// wise, *the lesser of this value and the holding register*. No wildcards will be used when reporting assets
|
||||
/// back.
|
||||
///
|
||||
/// Errors:
|
||||
#[codec(index = 6)]
|
||||
QueryHolding {
|
||||
#[codec(compact)]
|
||||
query_id: u64,
|
||||
dest: MultiLocation,
|
||||
assets: MultiAssetFilter,
|
||||
},
|
||||
|
||||
/// Pay for the execution of some XCM `instructions` and `orders` with up to `weight` picoseconds of execution time,
|
||||
/// paying for this with up to `fees` from the Holding Register.
|
||||
///
|
||||
/// - `fees`: The asset(s) to remove from holding to pay for fees.
|
||||
/// - `weight`: The amount of weight to purchase; this should be at least the shallow weight of `effects` and `xcm`.
|
||||
/// - `debt`: The amount of weight-debt already incurred to be paid off; this should be equal to the unpaid weight of
|
||||
/// any surrounding operations/orders.
|
||||
/// - `halt_on_error`: If `true`, the execution of the `orders` and `operations` will halt on the first failure. If
|
||||
/// `false`, then execution will continue regardless.
|
||||
/// - `orders`: Orders to be executed with the existing Holding Register; execution of these orders happens PRIOR to
|
||||
/// execution of the `operations`. The (shallow) weight for these must be paid for with the `weight` purchased.
|
||||
/// - `instructions`: XCM instructions to be executed outside of the context of the current Holding Register;
|
||||
/// execution of these instructions happens AFTER the execution of the `orders`. The (shallow) weight for these
|
||||
/// must be paid for with the `weight` purchased.
|
||||
/// Errors:
|
||||
#[codec(index = 7)]
|
||||
BuyExecution {
|
||||
fees: MultiAsset,
|
||||
weight: u64,
|
||||
debt: u64,
|
||||
halt_on_error: bool,
|
||||
orders: Vec<Order<Call>>,
|
||||
instructions: Vec<Xcm<Call>>,
|
||||
},
|
||||
}
|
||||
|
||||
pub mod opaque {
|
||||
pub type Order = super::Order<()>;
|
||||
}
|
||||
|
||||
impl<Call> Order<Call> {
|
||||
pub fn into<C>(self) -> Order<C> {
|
||||
Order::from(self)
|
||||
}
|
||||
pub fn from<C>(order: Order<C>) -> Self {
|
||||
use Order::*;
|
||||
match order {
|
||||
Noop => Noop,
|
||||
DepositAsset { assets, max_assets, beneficiary } =>
|
||||
DepositAsset { assets, max_assets, beneficiary },
|
||||
DepositReserveAsset { assets, max_assets, dest, effects } =>
|
||||
DepositReserveAsset { assets, max_assets, dest, effects },
|
||||
ExchangeAsset { give, receive } => ExchangeAsset { give, receive },
|
||||
InitiateReserveWithdraw { assets, reserve, effects } =>
|
||||
InitiateReserveWithdraw { assets, reserve, effects },
|
||||
InitiateTeleport { assets, dest, effects } =>
|
||||
InitiateTeleport { assets, dest, effects },
|
||||
QueryHolding { query_id, dest, assets } => QueryHolding { query_id, dest, assets },
|
||||
BuyExecution { fees, weight, debt, halt_on_error, orders, instructions } => {
|
||||
let orders = orders.into_iter().map(Order::from).collect();
|
||||
let instructions = instructions.into_iter().map(Xcm::from).collect();
|
||||
BuyExecution { fees, weight, debt, halt_on_error, orders, instructions }
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call> TryFrom<Order0<Call>> for Order<Call> {
|
||||
type Error = ();
|
||||
fn try_from(old: Order0<Call>) -> result::Result<Order<Call>, ()> {
|
||||
use Order::*;
|
||||
Ok(match old {
|
||||
Order0::Null => Noop,
|
||||
Order0::DepositAsset { assets, dest } =>
|
||||
DepositAsset { assets: assets.try_into()?, max_assets: 1, beneficiary: dest.into() },
|
||||
Order0::DepositReserveAsset { assets, dest, effects } => DepositReserveAsset {
|
||||
assets: assets.try_into()?,
|
||||
max_assets: 1,
|
||||
dest: dest.into(),
|
||||
effects: effects
|
||||
.into_iter()
|
||||
.map(Order::<()>::try_from)
|
||||
.collect::<result::Result<_, _>>()?,
|
||||
},
|
||||
Order0::ExchangeAsset { give, receive } =>
|
||||
ExchangeAsset { give: give.try_into()?, receive: receive.try_into()? },
|
||||
Order0::InitiateReserveWithdraw { assets, reserve, effects } =>
|
||||
InitiateReserveWithdraw {
|
||||
assets: assets.try_into()?,
|
||||
reserve: reserve.into(),
|
||||
effects: effects
|
||||
.into_iter()
|
||||
.map(Order::<()>::try_from)
|
||||
.collect::<result::Result<_, _>>()?,
|
||||
},
|
||||
Order0::InitiateTeleport { assets, dest, effects } => InitiateTeleport {
|
||||
assets: assets.try_into()?,
|
||||
dest: dest.into(),
|
||||
effects: effects
|
||||
.into_iter()
|
||||
.map(Order::<()>::try_from)
|
||||
.collect::<result::Result<_, _>>()?,
|
||||
},
|
||||
Order0::QueryHolding { query_id, dest, assets } =>
|
||||
QueryHolding { query_id, dest: dest.into(), assets: assets.try_into()? },
|
||||
Order0::BuyExecution { fees, weight, debt, halt_on_error, xcm } => {
|
||||
let instructions =
|
||||
xcm.into_iter().map(Xcm::<Call>::try_from).collect::<result::Result<_, _>>()?;
|
||||
BuyExecution {
|
||||
fees: fees.try_into()?,
|
||||
weight,
|
||||
debt,
|
||||
halt_on_error,
|
||||
orders: vec![],
|
||||
instructions,
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Cumulus.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Cross-Consensus Message format data structures.
|
||||
|
||||
use core::result;
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
|
||||
use super::{MultiLocation, Xcm};
|
||||
|
||||
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug)]
|
||||
pub enum Error {
|
||||
Undefined,
|
||||
/// An arithmetic overflow happened.
|
||||
Overflow,
|
||||
/// The operation is intentionally unsupported.
|
||||
Unimplemented,
|
||||
UnhandledXcmVersion,
|
||||
/// The implementation does not handle a given XCM.
|
||||
UnhandledXcmMessage,
|
||||
/// The implementation does not handle an effect present in an XCM.
|
||||
UnhandledEffect,
|
||||
EscalationOfPrivilege,
|
||||
UntrustedReserveLocation,
|
||||
UntrustedTeleportLocation,
|
||||
DestinationBufferOverflow,
|
||||
/// The message and destination was recognized as being reachable but the operation could not be completed.
|
||||
/// A human-readable explanation of the specific issue is provided.
|
||||
SendFailed(#[codec(skip)] &'static str),
|
||||
/// The message and destination combination was not recognized as being reachable.
|
||||
CannotReachDestination(MultiLocation, Xcm<()>),
|
||||
MultiLocationFull,
|
||||
FailedToDecode,
|
||||
BadOrigin,
|
||||
ExceedsMaxMessageSize,
|
||||
/// An asset transaction (like withdraw or deposit) failed.
|
||||
/// See implementers of the `TransactAsset` trait for sources.
|
||||
/// Causes can include type conversion failures between id or balance types.
|
||||
FailedToTransactAsset(#[codec(skip)] &'static str),
|
||||
/// Execution of the XCM would potentially result in a greater weight used than the pre-specified
|
||||
/// weight limit. The amount that is potentially required is the parameter.
|
||||
WeightLimitReached(Weight),
|
||||
/// An asset wildcard was passed where it was not expected (e.g. as the asset to withdraw in a
|
||||
/// `WithdrawAsset` XCM).
|
||||
Wildcard,
|
||||
/// The case where an XCM message has specified a optional weight limit and the weight required for
|
||||
/// processing is too great.
|
||||
///
|
||||
/// Used by:
|
||||
/// - `Transact`
|
||||
TooMuchWeightRequired,
|
||||
/// The fees specified by the XCM message were not found in the holding register.
|
||||
///
|
||||
/// Used by:
|
||||
/// - `BuyExecution`
|
||||
NotHoldingFees,
|
||||
/// The weight of an XCM message is not computable ahead of execution. This generally means at least part
|
||||
/// of the message is invalid, which could be due to it containing overly nested structures or an invalid
|
||||
/// nested data segment (e.g. for the call in `Transact`).
|
||||
WeightNotComputable,
|
||||
/// The XCM did not pass the barrier condition for execution. The barrier condition differs on different
|
||||
/// chains and in different circumstances, but generally it means that the conditions surrounding the message
|
||||
/// were not such that the chain considers the message worth spending time executing. Since most chains
|
||||
/// lift the barrier to execution on appropriate payment, presentation of an NFT voucher, or based on the
|
||||
/// message origin, it means that none of those were the case.
|
||||
Barrier,
|
||||
/// Indicates that it is not possible for a location to have an asset be withdrawn or transferred from its
|
||||
/// ownership. This probably means it doesn't own (enough of) it, but may also indicate that it is under a
|
||||
/// lock, hold, freeze or is otherwise unavailable.
|
||||
NotWithdrawable,
|
||||
/// Indicates that the consensus system cannot deposit an asset under the ownership of a particular location.
|
||||
LocationCannotHold,
|
||||
/// The assets given to purchase weight is are insufficient for the weight desired.
|
||||
TooExpensive,
|
||||
/// The given asset is not handled.
|
||||
AssetNotFound,
|
||||
/// The given message cannot be translated into a format that the destination can be expected to interpret.
|
||||
DestinationUnsupported,
|
||||
}
|
||||
|
||||
impl From<()> for Error {
|
||||
fn from(_: ()) -> Self {
|
||||
Self::Undefined
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result = result::Result<(), Error>;
|
||||
|
||||
/// Local weight type; execution time in picoseconds.
|
||||
pub type Weight = u64;
|
||||
|
||||
/// Outcome of an XCM execution.
|
||||
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug)]
|
||||
pub enum Outcome {
|
||||
/// Execution completed successfully; given weight was used.
|
||||
Complete(Weight),
|
||||
/// Execution started, but did not complete successfully due to the given error; given weight was used.
|
||||
Incomplete(Weight, Error),
|
||||
/// Execution did not start due to the given error.
|
||||
Error(Error),
|
||||
}
|
||||
|
||||
impl Outcome {
|
||||
pub fn ensure_complete(self) -> Result {
|
||||
match self {
|
||||
Outcome::Complete(_) => Ok(()),
|
||||
Outcome::Incomplete(_, e) => Err(e),
|
||||
Outcome::Error(e) => Err(e),
|
||||
}
|
||||
}
|
||||
pub fn ensure_execution(self) -> result::Result<Weight, Error> {
|
||||
match self {
|
||||
Outcome::Complete(w) => Ok(w),
|
||||
Outcome::Incomplete(w, _) => Ok(w),
|
||||
Outcome::Error(e) => Err(e),
|
||||
}
|
||||
}
|
||||
/// How much weight was used by the XCM execution attempt.
|
||||
pub fn weight_used(&self) -> Weight {
|
||||
match self {
|
||||
Outcome::Complete(w) => *w,
|
||||
Outcome::Incomplete(w, _) => *w,
|
||||
Outcome::Error(_) => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of XCM message executor.
|
||||
pub trait ExecuteXcm<Call> {
|
||||
/// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. The weight limit is
|
||||
/// a basic hard-limit and the implementation may place further restrictions or requirements on weight and
|
||||
/// other aspects.
|
||||
fn execute_xcm(origin: MultiLocation, message: Xcm<Call>, weight_limit: Weight) -> Outcome {
|
||||
log::debug!(
|
||||
target: "xcm::execute_xcm",
|
||||
"origin: {:?}, message: {:?}, weight_limit: {:?}",
|
||||
origin,
|
||||
message,
|
||||
weight_limit,
|
||||
);
|
||||
Self::execute_xcm_in_credit(origin, message, weight_limit, 0)
|
||||
}
|
||||
|
||||
/// Execute some XCM `message` from `origin` using no more than `weight_limit` weight.
|
||||
///
|
||||
/// Some amount of `weight_credit` may be provided which, depending on the implementation, may allow
|
||||
/// execution without associated payment.
|
||||
fn execute_xcm_in_credit(
|
||||
origin: MultiLocation,
|
||||
message: Xcm<Call>,
|
||||
weight_limit: Weight,
|
||||
weight_credit: Weight,
|
||||
) -> Outcome;
|
||||
}
|
||||
|
||||
impl<C> ExecuteXcm<C> for () {
|
||||
fn execute_xcm_in_credit(
|
||||
_origin: MultiLocation,
|
||||
_message: Xcm<C>,
|
||||
_weight_limit: Weight,
|
||||
_weight_credit: Weight,
|
||||
) -> Outcome {
|
||||
Outcome::Error(Error::Unimplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility for sending an XCM message.
|
||||
///
|
||||
/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return
|
||||
/// `CannotReachDestination` to pass the execution to the next sender item. Note that each `CannotReachDestination`
|
||||
/// might alter the destination and the XCM message for to the next router.
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// # use xcm::v1::{MultiLocation, Xcm, Junction, Error, OriginKind, SendXcm, Result};
|
||||
/// # use parity_scale_codec::Encode;
|
||||
///
|
||||
/// /// A sender that only passes the message through and does nothing.
|
||||
/// struct Sender1;
|
||||
/// impl SendXcm for Sender1 {
|
||||
/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
|
||||
/// return Err(Error::CannotReachDestination(destination, message))
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// A sender that accepts a message that has an X2 junction, otherwise stops the routing.
|
||||
/// struct Sender2;
|
||||
/// impl SendXcm for Sender2 {
|
||||
/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
|
||||
/// if let MultiLocation::X2(j1, j2) = destination {
|
||||
/// Ok(())
|
||||
/// } else {
|
||||
/// Err(Error::Undefined)
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// A sender that accepts a message from an X1 parent junction, passing through otherwise.
|
||||
/// struct Sender3;
|
||||
/// impl SendXcm for Sender3 {
|
||||
/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
|
||||
/// match destination {
|
||||
/// MultiLocation::X1(j) if j == Junction::Parent => Ok(()),
|
||||
/// _ => Err(Error::CannotReachDestination(destination, message)),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // A call to send via XCM. We don't really care about this.
|
||||
/// # fn main() {
|
||||
/// let call: Vec<u8> = ().encode();
|
||||
/// let message = Xcm::Transact { origin_type: OriginKind::Superuser, require_weight_at_most: 0, call: call.into() };
|
||||
/// let destination = MultiLocation::X1(Junction::Parent);
|
||||
///
|
||||
/// assert!(
|
||||
/// // Sender2 will block this.
|
||||
/// <(Sender1, Sender2, Sender3) as SendXcm>::send_xcm(destination.clone(), message.clone())
|
||||
/// .is_err()
|
||||
/// );
|
||||
///
|
||||
/// assert!(
|
||||
/// // Sender3 will catch this.
|
||||
/// <(Sender1, Sender3) as SendXcm>::send_xcm(destination.clone(), message.clone())
|
||||
/// .is_ok()
|
||||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
pub trait SendXcm {
|
||||
/// Send an XCM `message` to a given `destination`.
|
||||
///
|
||||
/// If it is not a destination which can be reached with this type but possibly could by others, then it *MUST*
|
||||
/// return `CannotReachDestination`. Any other error will cause the tuple implementation to exit early without
|
||||
/// trying other type fields.
|
||||
fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result;
|
||||
}
|
||||
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
impl SendXcm for Tuple {
|
||||
fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
|
||||
for_tuples!( #(
|
||||
// we shadow `destination` and `message` in each expansion for the next one.
|
||||
let (destination, message) = match Tuple::send_xcm(destination, message) {
|
||||
Err(Error::CannotReachDestination(d, m)) => (d, m),
|
||||
o @ _ => return o,
|
||||
};
|
||||
)* );
|
||||
Err(Error::CannotReachDestination(destination, message))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user