Unverified Commit 282731aa authored by Alexander Popiak's avatar Alexander Popiak Committed by GitHub
Browse files

XCM docs and tests (#2948)



* WIP

* add tests and docs for DoubleEncoded

* reformat parent_count

* add test for match_and_split

* fix append_with docs and add tests

* move Parachain enum variant to tuple

* Fix stuff

* add to append test

* simplify match

* formatting

* format and extend doc comments (including examples)

* fix typo

* add some doc comments

* add test for location inverter

* Add more tests/docs

* Fix build

* matches fungibles

* currency adapter.

* add more tests for location inverter

* extract max length magic number into constant

* adapters.

* Apply suggestions from code review

* Final touches.

* Repot and fixes

* Remove last todo

* Apply suggestions from code review
Co-authored-by: default avatarAlexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-builder/src/barriers.rs
Co-authored-by: default avatarAlexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-builder/src/barriers.rs
Co-authored-by: default avatarAlexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-builder/src/currency_adapter.rs
Co-authored-by: default avatarAlexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-builder/src/filter_asset_location.rs
Co-authored-by: default avatarAlexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-builder/src/matches_fungible.rs
Co-authored-by: default avatarAlexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-executor/src/traits/conversion.rs
Co-authored-by: default avatarAlexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-executor/src/traits/conversion.rs
Co-authored-by: default avatarAlexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-executor/src/traits/transact_asset.rs
Co-authored-by: default avatarAlexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-executor/src/traits/should_execute.rs
Co-authored-by: default avatarAlexander Popiak <alexander.popiak@parity.io>
Co-authored-by: default avatarkianenigma <kian@parity.io>
Co-authored-by: default avatarKian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: Shawn Tabrizi's avatarShawn Tabrizi <shawntabrizi@gmail.com>
parent 783a56fe
Pipeline #140467 failed with stages
in 19 minutes and 2 seconds
......@@ -137,7 +137,9 @@ impl Id {
}
}
/// Determine if a parachain is a system parachain or not.
pub trait IsSystem {
/// Returns `true` if a parachain is a system parachain, `false` otherwise.
fn is_system(&self) -> bool;
}
......
......@@ -17,6 +17,8 @@
use alloc::vec::Vec;
use parity_scale_codec::{Encode, Decode};
/// Wrapper around the encoded and decoded versions of a value.
/// Caches the decoded value once computed.
#[derive(Encode, Decode)]
#[codec(encode_bound())]
#[codec(decode_bound())]
......@@ -29,11 +31,12 @@ pub struct DoubleEncoded<T> {
impl<T> Clone for DoubleEncoded<T> {
fn clone(&self) -> Self { Self { encoded: self.encoded.clone(), decoded: None } }
}
impl<T> Eq for DoubleEncoded<T> {
}
impl<T> PartialEq for DoubleEncoded<T> {
fn eq(&self, other: &Self) -> bool { self.encoded.eq(&other.encoded) }
}
impl<T> Eq for DoubleEncoded<T> {}
impl<T> core::fmt::Debug for DoubleEncoded<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { self.encoded.fmt(f) }
}
......@@ -46,29 +49,66 @@ impl<T> From<Vec<u8>> for DoubleEncoded<T> {
impl<T> DoubleEncoded<T> {
pub fn into<S>(self) -> DoubleEncoded<S> { DoubleEncoded::from(self) }
pub fn from<S>(e: DoubleEncoded<S>) -> Self {
Self {
encoded: e.encoded,
decoded: None,
}
}
/// Provides an API similar to `AsRef` that provides access to the inner value.
/// `AsRef` implementation would expect an `&Option<T>` return type.
pub fn as_ref(&self) -> Option<&T> {
self.decoded.as_ref()
}
}
impl<T: Decode> DoubleEncoded<T> {
/// Decode the inner encoded value and store it.
/// Returns a reference to the value in case of success and `Err(())` in case the decoding fails.
pub fn ensure_decoded(&mut self) -> Result<&T, ()> {
if self.decoded.is_none() {
self.decoded = T::decode(&mut &self.encoded[..]).ok();
}
self.decoded.as_ref().ok_or(())
}
/// Move the decoded value out or (if not present) decode `encoded`.
pub fn take_decoded(&mut self) -> Result<T, ()> {
self.decoded.take().or_else(|| T::decode(&mut &self.encoded[..]).ok()).ok_or(())
}
/// Provides an API similar to `TryInto` that allows fallible conversion to the inner value type.
/// `TryInto` implementation would collide with std blanket implementation based on `TryFrom`.
pub fn try_into(mut self) -> Result<T, ()> {
self.ensure_decoded()?;
self.decoded.ok_or(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ensure_decoded_works() {
let val: u64 = 42;
let mut encoded: DoubleEncoded<_> = Encode::encode(&val).into();
assert_eq!(encoded.ensure_decoded(), Ok(&val));
}
#[test]
fn take_decoded_works() {
let val: u64 = 42;
let mut encoded: DoubleEncoded<_> = Encode::encode(&val).into();
assert_eq!(encoded.take_decoded(), Ok(val));
}
#[test]
fn try_into_works() {
let val: u64 = 42;
let encoded: DoubleEncoded<_> = Encode::encode(&val).into();
assert_eq!(encoded.try_into(), Ok(val));
}
}
......@@ -61,35 +61,34 @@ pub enum AssetInstance {
/// ### Abstract identifiers
///
/// Abstract identifiers are absolute identifiers that represent a notional asset which can exist within multiple
/// consensus systems. These tend to be simpler to deal with since their broad meaning is unchanged regardless stay
/// of the consensus system in which it is interpreted.
/// consensus systems. These tend to be simpler to deal with since their broad meaning is unchanged regardless stay of
/// the consensus system in which it is interpreted.
///
/// However, in the attempt to provide uniformity across consensus systems, they may conflate different instantiations
/// of some notional asset (e.g. the reserve asset and a local reserve-backed derivative of it) under the same name,
/// leading to confusion. It also implies that one notional asset is accounted for locally in only one way. This may
/// not be the case, e.g. where there are multiple bridge instances each providing a bridged "BTC" token yet none
/// being fungible between the others.
/// leading to confusion. It also implies that one notional asset is accounted for locally in only one way. This may not
/// be the case, e.g. where there are multiple bridge instances each providing a bridged "BTC" token yet none being
/// fungible between the others.
///
/// Since they are meant to be absolute and universal, a global registry is needed to ensure that name collisions
/// do not occur.
/// Since they are meant to be absolute and universal, a global registry is needed to ensure that name collisions do not
/// occur.
///
/// An abstract identifier is represented as a simple variable-size byte string. As of writing, no global registry
/// exists and no proposals have been put forth for asset labeling.
///
/// ### Concrete identifiers
///
/// Concrete identifiers are *relative identifiers* that specifically identify a single asset through its location in
/// a consensus system relative to the context interpreting. Use of a `MultiLocation` ensures that similar but non
/// fungible variants of the same underlying asset can be properly distinguished, and obviates the need for any kind
/// of central registry.
/// Concrete identifiers are *relative identifiers* that specifically identify a single asset through its location in a
/// consensus system relative to the context interpreting. Use of a `MultiLocation` ensures that similar but non
/// fungible variants of the same underlying asset can be properly distinguished, and obviates the need for any kind of
/// central registry.
///
/// The limitation is that the asset identifier cannot be trivially copied between consensus
/// systems and must instead be "re-anchored" whenever being moved to a new consensus system, using the two systems'
/// relative paths.
/// The limitation is that the asset identifier cannot be trivially copied between consensus systems and must instead be
/// "re-anchored" whenever being moved to a new consensus system, using the two systems' relative paths.
///
/// Throughout XCM, messages are authored such that *when interpreted from the receiver's point of view* they will
/// have the desired meaning/effect. This means that relative paths should always by constructed to be read from the
/// point of view of the receiving system, *which may be have a completely different meaning in the authoring system*.
/// Throughout XCM, messages are authored such that *when interpreted from the receiver's point of view* they will have
/// the desired meaning/effect. This means that relative paths should always by constructed to be read from the point of
/// view of the receiving system, *which may be have a completely different meaning in the authoring system*.
///
/// Concrete identifiers are the preferred way of identifying an asset since they are entirely unambiguous.
///
......@@ -99,8 +98,8 @@ pub enum AssetInstance {
///
/// - `<chain>/PalletInstance(<id>)` for a Frame chain with a single-asset pallet instance (such as an instance of the
/// Balances pallet).
/// - `<chain>/PalletInstance(<id>)/GeneralIndex(<index>)` for a Frame chain with an indexed multi-asset pallet
/// instance (such as an instance of the Assets pallet).
/// - `<chain>/PalletInstance(<id>)/GeneralIndex(<index>)` for a Frame chain with an indexed multi-asset pallet instance
/// (such as an instance of the Assets pallet).
/// - `<chain>/AccountId32` for an ERC-20-style single-asset smart-contract on a Frame-based contracts chain.
/// - `<chain>/AccountKey20` for an ERC-20-style single-asset smart-contract on an Ethereum-like chain.
///
......@@ -147,6 +146,9 @@ pub enum MultiAsset {
}
impl MultiAsset {
/// Returns `true` if the `MultiAsset` is a wildcard and can refer to classes of assets, instead of just one.
///
/// Typically can also be inferred by the name starting with `All`.
pub fn is_wildcard(&self) -> bool {
match self {
MultiAsset::None
......@@ -209,7 +211,6 @@ impl MultiAsset {
fn is_concrete_fungible(&self, id: &MultiLocation) -> bool {
match self {
MultiAsset::AllFungible => true,
MultiAsset::AllConcreteFungible { id: i }
| MultiAsset::ConcreteFungible { id: i, .. }
=> i == id,
......@@ -250,8 +251,13 @@ impl MultiAsset {
fn is_all(&self) -> bool { matches!(self, MultiAsset::All) }
/// 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 MultiAsset::*;
// Inner cannot be wild
if inner.is_wildcard() { return false }
// Everything contains nothing.
......@@ -273,15 +279,16 @@ impl MultiAsset {
AllConcreteNonFungible { class } => inner.is_concrete_non_fungible(class),
AllAbstractNonFungible { class } => inner.is_abstract_non_fungible(class),
ConcreteFungible { id, amount }
=> matches!(inner, ConcreteFungible { id: i , amount: a } if i == id && a >= amount),
AbstractFungible { id, amount }
=> matches!(inner, AbstractFungible { id: i , amount: a } if i == id && a >= amount),
ConcreteNonFungible { class, instance }
=> matches!(inner, ConcreteNonFungible { class: i , instance: a } if i == class && a == instance),
AbstractNonFungible { class, instance }
=> matches!(inner, AbstractNonFungible { class: i , instance: a } if i == class && a == instance),
ConcreteFungible { id, amount } => matches!(
inner,
ConcreteFungible { id: inner_id , amount: inner_amount } if inner_id == id && amount >= inner_amount
),
AbstractFungible { id, amount } => matches!(
inner,
AbstractFungible { id: inner_id , amount: inner_amount } if inner_id == id && amount >= inner_amount
),
ConcreteNonFungible { .. } => self == inner,
AbstractNonFungible { .. } => self == inner,
_ => false,
}
}
......@@ -313,3 +320,60 @@ impl TryFrom<VersionedMultiAsset> for MultiAsset {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn contains_works() {
use alloc::vec;
use MultiAsset::*;
// trivial case: all contains any non-wildcard.
assert!(All.contains(&None));
assert!(All.contains(&AbstractFungible { id: alloc::vec![99u8], amount: 1 }));
// trivial case: none contains nothing, except itself.
assert!(None.contains(&None));
assert!(!None.contains(&AllFungible));
assert!(!None.contains(&All));
// A bit more sneaky: Nothing can contain wildcard, even All ir the thing itself.
assert!(!All.contains(&All));
assert!(!All.contains(&AllFungible));
assert!(!AllFungible.contains(&AllFungible));
assert!(!AllNonFungible.contains(&AllNonFungible));
// For fungibles, containing is basically equality, or equal id with higher amount.
assert!(
!AbstractFungible { id: vec![99u8], amount: 99 }
.contains(&AbstractFungible { id: vec![1u8], amount: 99 })
);
assert!(
AbstractFungible { id: vec![99u8], amount: 99 }
.contains(&AbstractFungible { id: vec![99u8], amount: 99 })
);
assert!(
AbstractFungible { id: vec![99u8], amount: 99 }
.contains(&AbstractFungible { id: vec![99u8], amount: 9 })
);
assert!(
!AbstractFungible { id: vec![99u8], amount: 99 }
.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 } })
);
}
}
......@@ -66,6 +66,9 @@ pub enum MultiLocation {
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)
......@@ -442,8 +445,18 @@ impl MultiLocation {
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.
/// 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::v0::{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
......@@ -479,84 +492,69 @@ impl MultiLocation {
/// Returns the number of `Parent` junctions at the beginning of `self`.
pub fn parent_count(&self) -> usize {
use Junction::Parent;
match self {
MultiLocation::X8(
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent,
Junction::Parent, Junction::Parent, Junction::Parent
) => 8,
MultiLocation::X8(
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent,
Junction::Parent, Junction::Parent, ..
) => 7,
MultiLocation::X7(
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent,
Junction::Parent, Junction::Parent
) => 7,
MultiLocation::X8(
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent,
Junction::Parent, ..
) => 6,
MultiLocation::X7(
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent,
Junction::Parent, ..
) => 6,
MultiLocation::X6(
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent,
Junction::Parent
) => 6,
MultiLocation::X8(
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..
) => 5,
MultiLocation::X7(
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..
) => 5,
MultiLocation::X6(
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..
) => 5,
MultiLocation::X5(
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent
) => 5,
MultiLocation::X8(Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..) => 4,
MultiLocation::X7(Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..) => 4,
MultiLocation::X6(Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..) => 4,
MultiLocation::X5(Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..) => 4,
MultiLocation::X4(Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent) => 4,
MultiLocation::X8(Junction::Parent, Junction::Parent, Junction::Parent, ..) => 3,
MultiLocation::X7(Junction::Parent, Junction::Parent, Junction::Parent, ..) => 3,
MultiLocation::X6(Junction::Parent, Junction::Parent, Junction::Parent, ..) => 3,
MultiLocation::X5(Junction::Parent, Junction::Parent, Junction::Parent, ..) => 3,
MultiLocation::X4(Junction::Parent, Junction::Parent, Junction::Parent, ..) => 3,
MultiLocation::X3(Junction::Parent, Junction::Parent, Junction::Parent) => 3,
MultiLocation::X8(Junction::Parent, Junction::Parent, ..) => 2,
MultiLocation::X7(Junction::Parent, Junction::Parent, ..) => 2,
MultiLocation::X6(Junction::Parent, Junction::Parent, ..) => 2,
MultiLocation::X5(Junction::Parent, Junction::Parent, ..) => 2,
MultiLocation::X4(Junction::Parent, Junction::Parent, ..) => 2,
MultiLocation::X3(Junction::Parent, Junction::Parent, ..) => 2,
MultiLocation::X2(Junction::Parent, Junction::Parent) => 2,
MultiLocation::X8(Junction::Parent, ..) => 1,
MultiLocation::X7(Junction::Parent, ..) => 1,
MultiLocation::X6(Junction::Parent, ..) => 1,
MultiLocation::X5(Junction::Parent, ..) => 1,
MultiLocation::X4(Junction::Parent, ..) => 1,
MultiLocation::X3(Junction::Parent, ..) => 1,
MultiLocation::X2(Junction::Parent, ..) => 1,
MultiLocation::X1(Junction::Parent) => 1,
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,
}
}
/// Mutate `self` so that it is suffixed with `prefix`. The correct normalized form is returned, removing any
/// internal `Parent`s.
/// Mutate `self` so that it is suffixed with `suffix`. The correct normalized form is returned,
/// removing any internal [Non-Parent, `Parent`] combinations.
///
/// Does not modify `self` and returns `Err` with `prefix` in case of overflow.
/// Does not modify `self` and returns `Err` with `suffix` in case of overflow.
///
/// # Example
/// ```rust
/// # use xcm::v0::{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);
......@@ -570,21 +568,31 @@ impl MultiLocation {
}
}
/// Mutate `self` so that it is prefixed with `prefix`. The correct normalized form is returned, removing any
/// internal `Parent`s.
/// Mutate `self` so that it is prefixed with `prefix`. The correct normalized form is returned,
/// removing any internal [Non-Parent, `Parent`] combinations.
///
/// Does not modify `self` and returns `Err` with `prefix` in case of overflow.
///
/// # Example
/// ```rust
/// # use xcm::v0::{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 self_parents = self.parent_count();
let prefix_rest = prefix.len() - prefix.parent_count();
let skipped = self_parents.min(prefix_rest);
if self.len() + prefix.len() - 2 * skipped > 8 {
if self.len() + prefix.len() - 2 * skipped > MAX_MULTILOCATION_LENGTH {
return Err(prefix);
}
let mut prefix = prefix;
while match (prefix.last(), self.first()) {
(Some(x), Some(Junction::Parent)) if x != &Junction::Parent => {
(Some(x), Some(Junction::Parent)) if x.is_interior() => {
prefix.take_last();
self.take_first();
true
......@@ -593,13 +601,25 @@ impl MultiLocation {
} {}
for j in prefix.into_iter_rev() {
self.push_front(j).expect("len + prefix minus 2*skipped is less than 4; qed");
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
/// 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::v0::{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)
}
......@@ -619,3 +639,46 @@ impl TryFrom<VersionedMultiLocation> for MultiLocation {
}
}
}
#[cfg(test)]
mod tests {
use super::MultiLocation::*;
use crate::opaque::v0::{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));
}
}
......@@ -162,13 +162,72 @@ impl<C> ExecuteXcm<C> for () {
/// Utility for sending an XCM message.
///
/// These can be amalgamated in tuples to form sophisticated routing systems.
/// 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::v0::{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;