use super::{Junction, Junctions};
use crate::{
v2::MultiLocation as OldMultiLocation, v4::Location as NewMultiLocation, VersionedLocation,
};
use codec::{Decode, Encode, MaxEncodedLen};
use core::result;
use scale_info::TypeInfo;
#[derive(
Copy,
Clone,
Decode,
Encode,
Eq,
PartialEq,
Ord,
PartialOrd,
Debug,
TypeInfo,
MaxEncodedLen,
serde::Serialize,
serde::Deserialize,
)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub struct MultiLocation {
pub parents: u8,
pub interior: Junctions,
}
pub type Location = MultiLocation;
impl Default for MultiLocation {
fn default() -> Self {
Self { parents: 0, interior: Junctions::Here }
}
}
pub type InteriorMultiLocation = Junctions;
impl MultiLocation {
pub fn new(parents: u8, interior: impl Into<Junctions>) -> MultiLocation {
MultiLocation { parents, interior: interior.into() }
}
pub const fn into_versioned(self) -> VersionedLocation {
VersionedLocation::V3(self)
}
pub const fn here() -> MultiLocation {
MultiLocation { parents: 0, interior: Junctions::Here }
}
pub const fn parent() -> MultiLocation {
MultiLocation { parents: 1, interior: Junctions::Here }
}
pub const fn grandparent() -> MultiLocation {
MultiLocation { parents: 2, interior: Junctions::Here }
}
pub const fn ancestor(parents: u8) -> MultiLocation {
MultiLocation { parents, interior: Junctions::Here }
}
pub const fn is_here(&self) -> bool {
self.parents == 0 && self.interior.len() == 0
}
pub fn remove_network_id(&mut self) {
self.interior.remove_network_id();
}
pub fn interior(&self) -> &Junctions {
&self.interior
}
pub fn interior_mut(&mut self) -> &mut Junctions {
&mut self.interior
}
pub const fn parent_count(&self) -> u8 {
self.parents
}
pub const fn contains_parents_only(&self, count: u8) -> bool {
matches!(self.interior, Junctions::Here) && self.parents == count
}
pub const fn len(&self) -> usize {
self.parent_count() as usize + self.interior.len()
}
pub fn first_interior(&self) -> Option<&Junction> {
self.interior.first()
}
pub fn last(&self) -> Option<&Junction> {
self.interior.last()
}
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)
}
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)
}
pub fn push_interior(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
self.interior.push(new)
}
pub fn push_front_interior(
&mut self,
new: impl Into<Junction>,
) -> result::Result<(), Junction> {
self.interior.push_front(new)
}
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)),
}
}
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)),
}
}
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)
}
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)
}
pub fn dec_parent(&mut self) {
self.parents = self.parents.saturating_sub(1);
}
pub fn take_first_interior(&mut self) -> Option<Junction> {
self.interior.take_first()
}
pub fn take_last(&mut self) -> Option<Junction> {
self.interior.take_last()
}
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)
}
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)),
}
}
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)),
}
}
pub fn prepend_with(&mut self, prefix: impl Into<Self>) -> Result<(), Self> {
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)
}
while self.parents > 0 && prefix.take_last().is_some() {
self.dec_parent();
}
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(())
}
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)),
}
}
pub fn reanchor(
&mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<(), ()> {
let inverted_target = context.invert_target(target)?;
self.prepend_with(inverted_target).map_err(|_| ())?;
self.simplify(target.interior());
Ok(())
}
pub fn reanchored(
mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<Self, Self> {
match self.reanchor(target, context) {
Ok(()) => Ok(self),
Err(()) => Err(self),
}
}
pub fn simplify(&mut self, context: &Junctions) {
if context.len() < self.parents as usize {
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,
}
}
}
pub fn chain_location(&self) -> MultiLocation {
let mut clone = *self;
while let Some(j) = clone.last() {
if matches!(j, Junction::Parachain(_) | Junction::GlobalConsensus(_)) {
return clone
} else {
(clone, _) = clone.split_last_interior();
}
}
MultiLocation::new(clone.parents, Junctions::Here)
}
}
impl TryFrom<OldMultiLocation> for MultiLocation {
type Error = ();
fn try_from(x: OldMultiLocation) -> result::Result<Self, ()> {
Ok(MultiLocation { parents: x.parents, interior: x.interior.try_into()? })
}
}
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()?,
})
}
}
#[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 }
}
}
#[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 }
}
}
#[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 }
}
}
#[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, 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, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X2(Parachain(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(Parachain(1000), PalletInstance(42));
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: MultiLocation =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X3(OnlyChild, Parachain(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, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X3(Parachain(1000), PalletInstance(42), GeneralIndex(42));
let expected =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: MultiLocation =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X1(Parachain(1000));
let expected =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
location.simplify(&context);
assert_eq!(location, expected);
}
#[test]
fn reanchor_works() {
let mut id: MultiLocation = (Parent, Parachain(1000), GeneralIndex(42)).into();
let context = Parachain(2000).into();
let target = (Parent, Parachain(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(Parachain(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(Parachain(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(Parachain(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(Parachain(42)) };
assert_eq!(m.append_with(X2(PalletInstance(3), acc)), Ok(()));
assert_eq!(
m,
MultiLocation { parents: 1, interior: X3(Parachain(42), PalletInstance(3), acc) }
);
let acc = AccountIndex64 { network: None, index: 23 };
let m = MultiLocation {
parents: 254,
interior: X5(Parachain(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(Parachain(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(Parachain(42), AccountIndex64 { network: None, index: 23 })
}
);
let mut m = MultiLocation { parents: 254, interior: X1(Parachain(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(Parachain(42)) });
}
#[test]
fn double_ended_ref_iteration_works() {
let m = X3(Parachain(1000), Parachain(3), PalletInstance(5));
let mut iter = m.iter();
let first = iter.next().unwrap();
assert_eq!(first, &Parachain(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, &Parachain(3));
let res = Here
.pushed_with(*first)
.unwrap()
.pushed_with(*second)
.unwrap()
.pushed_with(*third)
.unwrap();
assert_eq!(m, res);
let m = Here;
let mut iter = m.iter();
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
}
#[test]
fn chain_location_works() {
let relay_to_local = MultiLocation::new(0, (PalletInstance(42), GeneralIndex(42)));
assert_eq!(relay_to_local.chain_location(), MultiLocation::here());
let relay_to_child =
MultiLocation::new(0, (Parachain(42), PalletInstance(42), GeneralIndex(42)));
let expected = MultiLocation::new(0, Parachain(42));
assert_eq!(relay_to_child.chain_location(), expected);
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);
let relay_to_remote_para = MultiLocation::new(
1,
(GlobalConsensus(Kusama), Parachain(42), PalletInstance(42), GeneralIndex(42)),
);
let expected = MultiLocation::new(1, (GlobalConsensus(Kusama), Parachain(42)));
assert_eq!(relay_to_remote_para.chain_location(), expected);
let para_to_relay = MultiLocation::new(1, (PalletInstance(42), GeneralIndex(42)));
assert_eq!(para_to_relay.chain_location(), MultiLocation::parent());
let para_to_sibling =
MultiLocation::new(1, (Parachain(42), PalletInstance(42), GeneralIndex(42)));
let expected = MultiLocation::new(1, Parachain(42));
assert_eq!(para_to_sibling.chain_location(), expected);
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);
let para_to_remote_para = MultiLocation::new(
2,
(GlobalConsensus(Kusama), Parachain(42), PalletInstance(42), GeneralIndex(42)),
);
let expected = MultiLocation::new(2, (GlobalConsensus(Kusama), Parachain(42)));
assert_eq!(para_to_remote_para.chain_location(), expected);
}
#[test]
fn conversion_from_other_types_works() {
use crate::v2;
fn takes_multilocation<Arg: Into<MultiLocation>>(_arg: Arg) {}
takes_multilocation(Parent);
takes_multilocation(Here);
takes_multilocation(X1(Parachain(42)));
takes_multilocation((Ancestor(255), PalletInstance(8)));
takes_multilocation((Ancestor(5), Parachain(1), PalletInstance(3)));
takes_multilocation((Ancestor(2), Here));
takes_multilocation(AncestorThen(
3,
X2(Parachain(43), AccountIndex64 { network: None, index: 155 }),
));
takes_multilocation((Parent, AccountId32 { network: None, id: [0; 32] }));
takes_multilocation((Parent, Here));
takes_multilocation(ParentThen(X1(Parachain(75))));
takes_multilocation([Parachain(100), PalletInstance(3)]);
assert_eq!(
v2::MultiLocation::from(v2::Junctions::Here).try_into(),
Ok(MultiLocation::here())
);
assert_eq!(v2::MultiLocation::from(v2::Parent).try_into(), Ok(MultiLocation::parent()));
assert_eq!(
v2::MultiLocation::from((v2::Parent, v2::Parent, v2::Junction::GeneralIndex(42u128),))
.try_into(),
Ok(MultiLocation { parents: 2, interior: X1(GeneralIndex(42u128)) }),
);
}
}