755 lines
27 KiB
Rust
755 lines
27 KiB
Rust
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// This file is part of Pezkuwi.
|
|
|
|
// Pezkuwi 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.
|
|
|
|
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//! XCM `MultiLocation` datatype.
|
|
|
|
use super::{Junction, Junctions};
|
|
use crate::{v4::Location as NewMultiLocation, VersionedLocation};
|
|
use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
|
|
use core::result;
|
|
use scale_info::TypeInfo;
|
|
|
|
/// 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 teyrchain.
|
|
/// - A layer-0 super-chain, e.g. the Pezkuwi 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 an integer number of parents specifying the number of times to
|
|
/// "escape" upwards into the containing consensus system and then a number of *junctions*, each
|
|
/// diving down and specifying some interior portion of state (which may be considered a
|
|
/// "sub-consensus" system).
|
|
///
|
|
/// This specific `MultiLocation` implementation uses a `Junctions` datatype which is a Rust `enum`
|
|
/// in order to make pattern matching easier. There are occasions where it is important to ensure
|
|
/// that a value is strictly an interior location, in those cases, `Junctions` may be used.
|
|
///
|
|
/// The `MultiLocation` value of `Null` simply refers to the interpreting consensus system.
|
|
#[derive(
|
|
Copy,
|
|
Clone,
|
|
Decode,
|
|
Encode,
|
|
DecodeWithMemTracking,
|
|
Eq,
|
|
PartialEq,
|
|
Ord,
|
|
PartialOrd,
|
|
Debug,
|
|
TypeInfo,
|
|
MaxEncodedLen,
|
|
serde::Serialize,
|
|
serde::Deserialize,
|
|
)]
|
|
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
|
|
pub struct MultiLocation {
|
|
/// The number of parent junctions at the beginning of this `MultiLocation`.
|
|
pub parents: u8,
|
|
/// The interior (i.e. non-parent) junctions that this `MultiLocation` contains.
|
|
pub interior: Junctions,
|
|
}
|
|
|
|
/// Type alias for a better transition to V4.
|
|
pub type Location = MultiLocation;
|
|
|
|
impl Default for MultiLocation {
|
|
fn default() -> Self {
|
|
Self { parents: 0, interior: Junctions::Here }
|
|
}
|
|
}
|
|
|
|
/// A relative location which is constrained to be an interior location of the context.
|
|
///
|
|
/// See also `MultiLocation`.
|
|
pub type InteriorMultiLocation = Junctions;
|
|
|
|
impl MultiLocation {
|
|
/// Creates a new `MultiLocation` with the given number of parents and interior junctions.
|
|
pub fn new(parents: u8, interior: impl Into<Junctions>) -> MultiLocation {
|
|
MultiLocation { parents, interior: interior.into() }
|
|
}
|
|
|
|
/// Consume `self` and return the equivalent `VersionedLocation` value.
|
|
pub const fn into_versioned(self) -> VersionedLocation {
|
|
VersionedLocation::V3(self)
|
|
}
|
|
|
|
/// Creates a new `MultiLocation` with 0 parents and a `Here` interior.
|
|
///
|
|
/// The resulting `MultiLocation` can be interpreted as the "current consensus system".
|
|
pub const fn here() -> MultiLocation {
|
|
MultiLocation { parents: 0, interior: Junctions::Here }
|
|
}
|
|
|
|
/// Creates a new `MultiLocation` which evaluates to the parent context.
|
|
pub const fn parent() -> MultiLocation {
|
|
MultiLocation { parents: 1, interior: Junctions::Here }
|
|
}
|
|
|
|
/// Creates a new `MultiLocation` which evaluates to the grand parent context.
|
|
pub const fn grandparent() -> MultiLocation {
|
|
MultiLocation { parents: 2, interior: Junctions::Here }
|
|
}
|
|
|
|
/// Creates a new `MultiLocation` with `parents` and an empty (`Here`) interior.
|
|
pub const fn ancestor(parents: u8) -> MultiLocation {
|
|
MultiLocation { parents, interior: Junctions::Here }
|
|
}
|
|
|
|
/// Whether the `MultiLocation` has no parents and has a `Here` interior.
|
|
pub const fn is_here(&self) -> bool {
|
|
self.parents == 0 && self.interior.len() == 0
|
|
}
|
|
|
|
/// Remove the `NetworkId` value in any interior `Junction`s.
|
|
pub fn remove_network_id(&mut self) {
|
|
self.interior.remove_network_id();
|
|
}
|
|
|
|
/// Return a reference to the interior field.
|
|
pub fn interior(&self) -> &Junctions {
|
|
&self.interior
|
|
}
|
|
|
|
/// Return a mutable reference to the interior field.
|
|
pub fn interior_mut(&mut self) -> &mut Junctions {
|
|
&mut self.interior
|
|
}
|
|
|
|
/// Returns the number of `Parent` junctions at the beginning of `self`.
|
|
pub const fn parent_count(&self) -> u8 {
|
|
self.parents
|
|
}
|
|
|
|
/// Returns boolean indicating whether `self` contains only the specified amount of
|
|
/// parents and no interior junctions.
|
|
pub const fn contains_parents_only(&self, count: u8) -> bool {
|
|
matches!(self.interior, Junctions::Here) && self.parents == count
|
|
}
|
|
|
|
/// Returns the number of parents and junctions in `self`.
|
|
pub const fn len(&self) -> usize {
|
|
self.parent_count() as usize + self.interior.len()
|
|
}
|
|
|
|
/// Returns the first interior junction, or `None` if the location is empty or contains only
|
|
/// parents.
|
|
pub fn first_interior(&self) -> Option<&Junction> {
|
|
self.interior.first()
|
|
}
|
|
|
|
/// Returns last junction, or `None` if the location is empty or contains only parents.
|
|
pub fn last(&self) -> Option<&Junction> {
|
|
self.interior.last()
|
|
}
|
|
|
|
/// Splits off the first interior 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_interior(self) -> (MultiLocation, Option<Junction>) {
|
|
let MultiLocation { parents, interior: junctions } = self;
|
|
let (suffix, first) = junctions.split_first();
|
|
let multilocation = MultiLocation { parents, interior: suffix };
|
|
(multilocation, first)
|
|
}
|
|
|
|
/// Splits off the last interior junction, returning the remaining prefix (first item in tuple)
|
|
/// and the last element (second item in tuple) or `None` if it was empty or if `self` only
|
|
/// contains parents.
|
|
pub fn split_last_interior(self) -> (MultiLocation, Option<Junction>) {
|
|
let MultiLocation { parents, interior: junctions } = self;
|
|
let (prefix, last) = junctions.split_last();
|
|
let multilocation = MultiLocation { parents, interior: prefix };
|
|
(multilocation, last)
|
|
}
|
|
|
|
/// Mutates `self`, suffixing its interior junctions with `new`. Returns `Err` with `new` in
|
|
/// case of overflow.
|
|
pub fn push_interior(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
|
|
self.interior.push(new)
|
|
}
|
|
|
|
/// Mutates `self`, prefixing its interior junctions with `new`. Returns `Err` with `new` in
|
|
/// case of overflow.
|
|
pub fn push_front_interior(
|
|
&mut self,
|
|
new: impl Into<Junction>,
|
|
) -> result::Result<(), Junction> {
|
|
self.interior.push_front(new)
|
|
}
|
|
|
|
/// 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_interior(
|
|
self,
|
|
new: impl Into<Junction>,
|
|
) -> result::Result<Self, (Self, Junction)> {
|
|
match self.interior.pushed_with(new) {
|
|
Ok(i) => Ok(MultiLocation { interior: i, parents: self.parents }),
|
|
Err((i, j)) => Err((MultiLocation { interior: i, parents: self.parents }, j)),
|
|
}
|
|
}
|
|
|
|
/// 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_interior(
|
|
self,
|
|
new: impl Into<Junction>,
|
|
) -> result::Result<Self, (Self, Junction)> {
|
|
match self.interior.pushed_front_with(new) {
|
|
Ok(i) => Ok(MultiLocation { interior: i, parents: self.parents }),
|
|
Err((i, j)) => Err((MultiLocation { interior: i, parents: self.parents }, j)),
|
|
}
|
|
}
|
|
|
|
/// Returns the junction at index `i`, or `None` if the location is a parent or if the location
|
|
/// does not contain that many elements.
|
|
pub fn at(&self, i: usize) -> Option<&Junction> {
|
|
let num_parents = self.parents as usize;
|
|
if i < num_parents {
|
|
return None;
|
|
}
|
|
self.interior.at(i - num_parents)
|
|
}
|
|
|
|
/// Returns a mutable reference to the junction at index `i`, or `None` if the location is a
|
|
/// parent or if it doesn't contain that many elements.
|
|
pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> {
|
|
let num_parents = self.parents as usize;
|
|
if i < num_parents {
|
|
return None;
|
|
}
|
|
self.interior.at_mut(i - num_parents)
|
|
}
|
|
|
|
/// Decrements the parent count by 1.
|
|
pub fn dec_parent(&mut self) {
|
|
self.parents = self.parents.saturating_sub(1);
|
|
}
|
|
|
|
/// Removes the first interior junction from `self`, returning it
|
|
/// (or `None` if it was empty or if `self` contains only parents).
|
|
pub fn take_first_interior(&mut self) -> Option<Junction> {
|
|
self.interior.take_first()
|
|
}
|
|
|
|
/// Removes the last element from `interior`, returning it (or `None` if it was empty or if
|
|
/// `self` only contains parents).
|
|
pub fn take_last(&mut self) -> Option<Junction> {
|
|
self.interior.take_last()
|
|
}
|
|
|
|
/// Ensures that `self` has the same number of parents as `prefix`, its junctions begins with
|
|
/// the junctions of `prefix` and that it has a single `Junction` item following.
|
|
/// If so, returns a reference to this `Junction` item.
|
|
///
|
|
/// # Example
|
|
/// ```rust
|
|
/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation};
|
|
/// let mut m = MultiLocation::new(1, X2(PalletInstance(3), OnlyChild));
|
|
/// assert_eq!(
|
|
/// m.match_and_split(&MultiLocation::new(1, X1(PalletInstance(3)))),
|
|
/// Some(&OnlyChild),
|
|
/// );
|
|
/// assert_eq!(m.match_and_split(&MultiLocation::new(1, Here)), None);
|
|
/// ```
|
|
pub fn match_and_split(&self, prefix: &MultiLocation) -> Option<&Junction> {
|
|
if self.parents != prefix.parents {
|
|
return None;
|
|
}
|
|
self.interior.match_and_split(&prefix.interior)
|
|
}
|
|
|
|
pub fn starts_with(&self, prefix: &MultiLocation) -> bool {
|
|
self.parents == prefix.parents && self.interior.starts_with(&prefix.interior)
|
|
}
|
|
|
|
/// Mutate `self` so that it is suffixed with `suffix`.
|
|
///
|
|
/// Does not modify `self` and returns `Err` with `suffix` in case of overflow.
|
|
///
|
|
/// # Example
|
|
/// ```rust
|
|
/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
|
|
/// let mut m: MultiLocation = (Parent, Teyrchain(21), 69u64).into();
|
|
/// assert_eq!(m.append_with((Parent, PalletInstance(3))), Ok(()));
|
|
/// assert_eq!(m, MultiLocation::new(1, X2(Teyrchain(21), PalletInstance(3))));
|
|
/// ```
|
|
pub fn append_with(&mut self, suffix: impl Into<Self>) -> Result<(), Self> {
|
|
let prefix = core::mem::replace(self, suffix.into());
|
|
match self.prepend_with(prefix) {
|
|
Ok(()) => Ok(()),
|
|
Err(prefix) => Err(core::mem::replace(self, prefix)),
|
|
}
|
|
}
|
|
|
|
/// Consume `self` and return its value suffixed with `suffix`.
|
|
///
|
|
/// Returns `Err` with the original value of `self` and `suffix` in case of overflow.
|
|
///
|
|
/// # Example
|
|
/// ```rust
|
|
/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
|
|
/// let mut m: MultiLocation = (Parent, Teyrchain(21), 69u64).into();
|
|
/// let r = m.appended_with((Parent, PalletInstance(3))).unwrap();
|
|
/// assert_eq!(r, MultiLocation::new(1, X2(Teyrchain(21), PalletInstance(3))));
|
|
/// ```
|
|
pub fn appended_with(mut self, suffix: impl Into<Self>) -> Result<Self, (Self, Self)> {
|
|
match self.append_with(suffix) {
|
|
Ok(()) => Ok(self),
|
|
Err(suffix) => Err((self, suffix)),
|
|
}
|
|
}
|
|
|
|
/// Mutate `self` so that it is prefixed with `prefix`.
|
|
///
|
|
/// Does not modify `self` and returns `Err` with `prefix` in case of overflow.
|
|
///
|
|
/// # Example
|
|
/// ```rust
|
|
/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
|
|
/// let mut m: MultiLocation = (Parent, Parent, PalletInstance(3)).into();
|
|
/// assert_eq!(m.prepend_with((Parent, Teyrchain(21), OnlyChild)), Ok(()));
|
|
/// assert_eq!(m, MultiLocation::new(1, X1(PalletInstance(3))));
|
|
/// ```
|
|
pub fn prepend_with(&mut self, prefix: impl Into<Self>) -> Result<(), Self> {
|
|
// prefix self (suffix)
|
|
// P .. P I .. I p .. p i .. i
|
|
let mut prefix = prefix.into();
|
|
let prepend_interior = prefix.interior.len().saturating_sub(self.parents as usize);
|
|
let final_interior = self.interior.len().saturating_add(prepend_interior);
|
|
if final_interior > super::junctions::MAX_JUNCTIONS {
|
|
return Err(prefix);
|
|
}
|
|
let suffix_parents = (self.parents as usize).saturating_sub(prefix.interior.len());
|
|
let final_parents = (prefix.parents as usize).saturating_add(suffix_parents);
|
|
if final_parents > 255 {
|
|
return Err(prefix);
|
|
}
|
|
|
|
// cancel out the final item on the prefix interior for one of the suffix's parents.
|
|
while self.parents > 0 && prefix.take_last().is_some() {
|
|
self.dec_parent();
|
|
}
|
|
|
|
// now we have either removed all suffix's parents or prefix interior.
|
|
// this means we can combine the prefix's and suffix's remaining parents/interior since
|
|
// we know that with at least one empty, the overall order will be respected:
|
|
// prefix self (suffix)
|
|
// P .. P (I) p .. p i .. i => P + p .. (no I) i
|
|
// -- or --
|
|
// P .. P I .. I (p) i .. i => P (no p) .. I + i
|
|
|
|
self.parents = self.parents.saturating_add(prefix.parents);
|
|
for j in prefix.interior.into_iter().rev() {
|
|
self.push_front_interior(j)
|
|
.expect("final_interior no greater than MAX_JUNCTIONS; qed");
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Consume `self` and return its value prefixed with `prefix`.
|
|
///
|
|
/// Returns `Err` with the original value of `self` and `prefix` in case of overflow.
|
|
///
|
|
/// # Example
|
|
/// ```rust
|
|
/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
|
|
/// let m: MultiLocation = (Parent, Parent, PalletInstance(3)).into();
|
|
/// let r = m.prepended_with((Parent, Teyrchain(21), OnlyChild)).unwrap();
|
|
/// assert_eq!(r, MultiLocation::new(1, X1(PalletInstance(3))));
|
|
/// ```
|
|
pub fn prepended_with(mut self, prefix: impl Into<Self>) -> Result<Self, (Self, Self)> {
|
|
match self.prepend_with(prefix) {
|
|
Ok(()) => Ok(self),
|
|
Err(prefix) => Err((self, prefix)),
|
|
}
|
|
}
|
|
|
|
/// Mutate `self` so that it represents the same location from the point of view of `target`.
|
|
/// The context of `self` is provided as `context`.
|
|
///
|
|
/// Does not modify `self` in case of overflow.
|
|
pub fn reanchor(
|
|
&mut self,
|
|
target: &MultiLocation,
|
|
context: InteriorMultiLocation,
|
|
) -> Result<(), ()> {
|
|
// TODO: https://github.com/paritytech/polkadot/issues/4489 Optimize this.
|
|
|
|
// 1. Use our `context` to figure out how the `target` would address us.
|
|
let inverted_target = context.invert_target(target)?;
|
|
|
|
// 2. Prepend `inverted_target` to `self` to get self's location from the perspective of
|
|
// `target`.
|
|
self.prepend_with(inverted_target).map_err(|_| ())?;
|
|
|
|
// 3. Given that we know some of `target` context, ensure that any parents in `self` are
|
|
// strictly needed.
|
|
self.simplify(target.interior());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Consume `self` and return a new value representing the same location from the point of view
|
|
/// of `target`. The context of `self` is provided as `context`.
|
|
///
|
|
/// Returns the original `self` in case of overflow.
|
|
pub fn reanchored(
|
|
mut self,
|
|
target: &MultiLocation,
|
|
context: InteriorMultiLocation,
|
|
) -> Result<Self, Self> {
|
|
match self.reanchor(target, context) {
|
|
Ok(()) => Ok(self),
|
|
Err(()) => Err(self),
|
|
}
|
|
}
|
|
|
|
/// Remove any unneeded parents/junctions in `self` based on the given context it will be
|
|
/// interpreted in.
|
|
pub fn simplify(&mut self, context: &Junctions) {
|
|
if context.len() < self.parents as usize {
|
|
// Not enough context
|
|
return;
|
|
}
|
|
while self.parents > 0 {
|
|
let maybe = context.at(context.len() - (self.parents as usize));
|
|
match (self.interior.first(), maybe) {
|
|
(Some(i), Some(j)) if i == j => {
|
|
self.interior.take_first();
|
|
self.parents -= 1;
|
|
},
|
|
_ => break,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Return the MultiLocation subsection identifying the chain that `self` points to.
|
|
pub fn chain_location(&self) -> MultiLocation {
|
|
let mut clone = *self;
|
|
// start popping junctions until we reach chain identifier
|
|
while let Some(j) = clone.last() {
|
|
if matches!(j, Junction::Teyrchain(_) | Junction::GlobalConsensus(_)) {
|
|
// return chain subsection
|
|
return clone;
|
|
} else {
|
|
(clone, _) = clone.split_last_interior();
|
|
}
|
|
}
|
|
MultiLocation::new(clone.parents, Junctions::Here)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<NewMultiLocation> for Option<MultiLocation> {
|
|
type Error = ();
|
|
fn try_from(new: NewMultiLocation) -> result::Result<Self, Self::Error> {
|
|
Ok(Some(MultiLocation::try_from(new)?))
|
|
}
|
|
}
|
|
|
|
impl TryFrom<NewMultiLocation> for MultiLocation {
|
|
type Error = ();
|
|
fn try_from(new: NewMultiLocation) -> result::Result<Self, ()> {
|
|
Ok(MultiLocation {
|
|
parents: new.parent_count(),
|
|
interior: new.interior().clone().try_into()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// A unit struct which can be converted into a `MultiLocation` of `parents` value 1.
|
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
|
pub struct Parent;
|
|
impl From<Parent> for MultiLocation {
|
|
fn from(_: Parent) -> Self {
|
|
MultiLocation { parents: 1, interior: Junctions::Here }
|
|
}
|
|
}
|
|
|
|
/// A tuple struct which can be converted into a `MultiLocation` of `parents` value 1 with the inner
|
|
/// interior.
|
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
|
pub struct ParentThen(pub Junctions);
|
|
impl From<ParentThen> for MultiLocation {
|
|
fn from(ParentThen(interior): ParentThen) -> Self {
|
|
MultiLocation { parents: 1, interior }
|
|
}
|
|
}
|
|
|
|
/// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value.
|
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
|
pub struct Ancestor(pub u8);
|
|
impl From<Ancestor> for MultiLocation {
|
|
fn from(Ancestor(parents): Ancestor) -> Self {
|
|
MultiLocation { parents, interior: Junctions::Here }
|
|
}
|
|
}
|
|
|
|
/// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value and the
|
|
/// inner interior.
|
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
|
pub struct AncestorThen<Interior>(pub u8, pub Interior);
|
|
impl<Interior: Into<Junctions>> From<AncestorThen<Interior>> for MultiLocation {
|
|
fn from(AncestorThen(parents, interior): AncestorThen<Interior>) -> Self {
|
|
MultiLocation { parents, interior: interior.into() }
|
|
}
|
|
}
|
|
|
|
xcm_procedural::impl_conversion_functions_for_multilocation_v3!();
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::v3::prelude::*;
|
|
use codec::{Decode, Encode};
|
|
|
|
#[test]
|
|
fn conversion_works() {
|
|
let x: MultiLocation = Parent.into();
|
|
assert_eq!(x, MultiLocation { parents: 1, interior: Here });
|
|
// let x: MultiLocation = (Parent,).into();
|
|
// assert_eq!(x, MultiLocation { parents: 1, interior: Here });
|
|
// let x: MultiLocation = (Parent, Parent).into();
|
|
// assert_eq!(x, MultiLocation { parents: 2, interior: Here });
|
|
let x: MultiLocation = (Parent, Parent, OnlyChild).into();
|
|
assert_eq!(x, MultiLocation { parents: 2, interior: OnlyChild.into() });
|
|
let x: MultiLocation = OnlyChild.into();
|
|
assert_eq!(x, MultiLocation { parents: 0, interior: OnlyChild.into() });
|
|
let x: MultiLocation = (OnlyChild,).into();
|
|
assert_eq!(x, MultiLocation { parents: 0, interior: OnlyChild.into() });
|
|
}
|
|
|
|
#[test]
|
|
fn simplify_basic_works() {
|
|
let mut location: MultiLocation =
|
|
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
|
|
let context = X2(Teyrchain(1000), PalletInstance(42));
|
|
let expected = GeneralIndex(69).into();
|
|
location.simplify(&context);
|
|
assert_eq!(location, expected);
|
|
|
|
let mut location: MultiLocation = (Parent, PalletInstance(42), GeneralIndex(69)).into();
|
|
let context = X1(PalletInstance(42));
|
|
let expected = GeneralIndex(69).into();
|
|
location.simplify(&context);
|
|
assert_eq!(location, expected);
|
|
|
|
let mut location: MultiLocation = (Parent, PalletInstance(42), GeneralIndex(69)).into();
|
|
let context = X2(Teyrchain(1000), PalletInstance(42));
|
|
let expected = GeneralIndex(69).into();
|
|
location.simplify(&context);
|
|
assert_eq!(location, expected);
|
|
|
|
let mut location: MultiLocation =
|
|
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
|
|
let context = X3(OnlyChild, Teyrchain(1000), PalletInstance(42));
|
|
let expected = GeneralIndex(69).into();
|
|
location.simplify(&context);
|
|
assert_eq!(location, expected);
|
|
}
|
|
|
|
#[test]
|
|
fn simplify_incompatible_location_fails() {
|
|
let mut location: MultiLocation =
|
|
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
|
|
let context = X3(Teyrchain(1000), PalletInstance(42), GeneralIndex(42));
|
|
let expected =
|
|
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
|
|
location.simplify(&context);
|
|
assert_eq!(location, expected);
|
|
|
|
let mut location: MultiLocation =
|
|
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
|
|
let context = X1(Teyrchain(1000));
|
|
let expected =
|
|
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
|
|
location.simplify(&context);
|
|
assert_eq!(location, expected);
|
|
}
|
|
|
|
#[test]
|
|
fn reanchor_works() {
|
|
let mut id: MultiLocation = (Parent, Teyrchain(1000), GeneralIndex(42)).into();
|
|
let context = Teyrchain(2000).into();
|
|
let target = (Parent, Teyrchain(1000)).into();
|
|
let expected = GeneralIndex(42).into();
|
|
id.reanchor(&target, context).unwrap();
|
|
assert_eq!(id, expected);
|
|
}
|
|
|
|
#[test]
|
|
fn encode_and_decode_works() {
|
|
let m = MultiLocation {
|
|
parents: 1,
|
|
interior: X2(Teyrchain(42), AccountIndex64 { network: None, index: 23 }),
|
|
};
|
|
let encoded = m.encode();
|
|
assert_eq!(encoded, [1, 2, 0, 168, 2, 0, 92].to_vec());
|
|
let decoded = MultiLocation::decode(&mut &encoded[..]);
|
|
assert_eq!(decoded, Ok(m));
|
|
}
|
|
|
|
#[test]
|
|
fn match_and_split_works() {
|
|
let m = MultiLocation {
|
|
parents: 1,
|
|
interior: X2(Teyrchain(42), AccountIndex64 { network: None, index: 23 }),
|
|
};
|
|
assert_eq!(m.match_and_split(&MultiLocation { parents: 1, interior: Here }), None);
|
|
assert_eq!(
|
|
m.match_and_split(&MultiLocation { parents: 1, interior: X1(Teyrchain(42)) }),
|
|
Some(&AccountIndex64 { network: None, index: 23 })
|
|
);
|
|
assert_eq!(m.match_and_split(&m), None);
|
|
}
|
|
|
|
#[test]
|
|
fn append_with_works() {
|
|
let acc = AccountIndex64 { network: None, index: 23 };
|
|
let mut m = MultiLocation { parents: 1, interior: X1(Teyrchain(42)) };
|
|
assert_eq!(m.append_with(X2(PalletInstance(3), acc)), Ok(()));
|
|
assert_eq!(
|
|
m,
|
|
MultiLocation { parents: 1, interior: X3(Teyrchain(42), PalletInstance(3), acc) }
|
|
);
|
|
|
|
// cannot append to create overly long multilocation
|
|
let acc = AccountIndex64 { network: None, index: 23 };
|
|
let m = MultiLocation {
|
|
parents: 254,
|
|
interior: X5(Teyrchain(42), OnlyChild, OnlyChild, OnlyChild, OnlyChild),
|
|
};
|
|
let suffix: MultiLocation = (PalletInstance(3), acc, OnlyChild, OnlyChild).into();
|
|
assert_eq!(m.clone().append_with(suffix), Err(suffix));
|
|
}
|
|
|
|
#[test]
|
|
fn prepend_with_works() {
|
|
let mut m = MultiLocation {
|
|
parents: 1,
|
|
interior: X2(Teyrchain(42), AccountIndex64 { network: None, index: 23 }),
|
|
};
|
|
assert_eq!(m.prepend_with(MultiLocation { parents: 1, interior: X1(OnlyChild) }), Ok(()));
|
|
assert_eq!(
|
|
m,
|
|
MultiLocation {
|
|
parents: 1,
|
|
interior: X2(Teyrchain(42), AccountIndex64 { network: None, index: 23 })
|
|
}
|
|
);
|
|
|
|
// cannot prepend to create overly long multilocation
|
|
let mut m = MultiLocation { parents: 254, interior: X1(Teyrchain(42)) };
|
|
let prefix = MultiLocation { parents: 2, interior: Here };
|
|
assert_eq!(m.prepend_with(prefix), Err(prefix));
|
|
|
|
let prefix = MultiLocation { parents: 1, interior: Here };
|
|
assert_eq!(m.prepend_with(prefix), Ok(()));
|
|
assert_eq!(m, MultiLocation { parents: 255, interior: X1(Teyrchain(42)) });
|
|
}
|
|
|
|
#[test]
|
|
fn double_ended_ref_iteration_works() {
|
|
let m = X3(Teyrchain(1000), Teyrchain(3), PalletInstance(5));
|
|
let mut iter = m.iter();
|
|
|
|
let first = iter.next().unwrap();
|
|
assert_eq!(first, &Teyrchain(1000));
|
|
let third = iter.next_back().unwrap();
|
|
assert_eq!(third, &PalletInstance(5));
|
|
let second = iter.next_back().unwrap();
|
|
assert_eq!(iter.next(), None);
|
|
assert_eq!(iter.next_back(), None);
|
|
assert_eq!(second, &Teyrchain(3));
|
|
|
|
let res = Here
|
|
.pushed_with(*first)
|
|
.unwrap()
|
|
.pushed_with(*second)
|
|
.unwrap()
|
|
.pushed_with(*third)
|
|
.unwrap();
|
|
assert_eq!(m, res);
|
|
|
|
// make sure there's no funny business with the 0 indexing
|
|
let m = Here;
|
|
let mut iter = m.iter();
|
|
|
|
assert_eq!(iter.next(), None);
|
|
assert_eq!(iter.next_back(), None);
|
|
}
|
|
|
|
#[test]
|
|
fn chain_location_works() {
|
|
// Relay-chain or teyrchain context pointing to local resource,
|
|
let relay_to_local = MultiLocation::new(0, (PalletInstance(42), GeneralIndex(42)));
|
|
assert_eq!(relay_to_local.chain_location(), MultiLocation::here());
|
|
|
|
// Relay-chain context pointing to child teyrchain,
|
|
let relay_to_child =
|
|
MultiLocation::new(0, (Teyrchain(42), PalletInstance(42), GeneralIndex(42)));
|
|
let expected = MultiLocation::new(0, Teyrchain(42));
|
|
assert_eq!(relay_to_child.chain_location(), expected);
|
|
|
|
// Relay-chain context pointing to different consensus relay,
|
|
let relay_to_remote_relay =
|
|
MultiLocation::new(1, (GlobalConsensus(Kusama), PalletInstance(42), GeneralIndex(42)));
|
|
let expected = MultiLocation::new(1, GlobalConsensus(Kusama));
|
|
assert_eq!(relay_to_remote_relay.chain_location(), expected);
|
|
|
|
// Relay-chain context pointing to different consensus teyrchain,
|
|
let relay_to_remote_para = MultiLocation::new(
|
|
1,
|
|
(GlobalConsensus(Kusama), Teyrchain(42), PalletInstance(42), GeneralIndex(42)),
|
|
);
|
|
let expected = MultiLocation::new(1, (GlobalConsensus(Kusama), Teyrchain(42)));
|
|
assert_eq!(relay_to_remote_para.chain_location(), expected);
|
|
|
|
// Teyrchain context pointing to relay chain,
|
|
let para_to_relay = MultiLocation::new(1, (PalletInstance(42), GeneralIndex(42)));
|
|
assert_eq!(para_to_relay.chain_location(), MultiLocation::parent());
|
|
|
|
// Teyrchain context pointing to sibling teyrchain,
|
|
let para_to_sibling =
|
|
MultiLocation::new(1, (Teyrchain(42), PalletInstance(42), GeneralIndex(42)));
|
|
let expected = MultiLocation::new(1, Teyrchain(42));
|
|
assert_eq!(para_to_sibling.chain_location(), expected);
|
|
|
|
// Teyrchain context pointing to different consensus relay,
|
|
let para_to_remote_relay =
|
|
MultiLocation::new(2, (GlobalConsensus(Kusama), PalletInstance(42), GeneralIndex(42)));
|
|
let expected = MultiLocation::new(2, GlobalConsensus(Kusama));
|
|
assert_eq!(para_to_remote_relay.chain_location(), expected);
|
|
|
|
// Teyrchain context pointing to different consensus teyrchain,
|
|
let para_to_remote_para = MultiLocation::new(
|
|
2,
|
|
(GlobalConsensus(Kusama), Teyrchain(42), PalletInstance(42), GeneralIndex(42)),
|
|
);
|
|
let expected = MultiLocation::new(2, (GlobalConsensus(Kusama), Teyrchain(42)));
|
|
assert_eq!(para_to_remote_para.chain_location(), expected);
|
|
}
|
|
}
|