use super::{traits::Reanchorable, Junction, Junctions};
use crate::{v3::MultiLocation as OldLocation, VersionedLocation};
use codec::{Decode, Encode, MaxEncodedLen};
use core::result;
use scale_info::TypeInfo;
#[derive(
Clone,
Decode,
Encode,
Eq,
PartialEq,
Ord,
PartialOrd,
Debug,
TypeInfo,
MaxEncodedLen,
serde::Serialize,
serde::Deserialize,
)]
pub struct Location {
pub parents: u8,
pub interior: Junctions,
}
impl Default for Location {
fn default() -> Self {
Self { parents: 0, interior: Junctions::Here }
}
}
pub type InteriorLocation = Junctions;
impl Location {
pub fn new(parents: u8, interior: impl Into<Junctions>) -> Location {
Location { parents, interior: interior.into() }
}
pub const fn into_versioned(self) -> VersionedLocation {
VersionedLocation::V4(self)
}
pub const fn here() -> Location {
Location { parents: 0, interior: Junctions::Here }
}
pub const fn parent() -> Location {
Location { parents: 1, interior: Junctions::Here }
}
pub const fn ancestor(parents: u8) -> Location {
Location { parents, interior: Junctions::Here }
}
pub 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 fn unpack(&self) -> (u8, &[Junction]) {
(self.parents, self.interior.as_slice())
}
pub const fn contains_parents_only(&self, count: u8) -> bool {
matches!(self.interior, Junctions::Here) && self.parents == count
}
pub 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) -> (Location, Option<Junction>) {
let Location { parents, interior: junctions } = self;
let (suffix, first) = junctions.split_first();
let location = Location { parents, interior: suffix };
(location, first)
}
pub fn split_last_interior(self) -> (Location, Option<Junction>) {
let Location { parents, interior: junctions } = self;
let (prefix, last) = junctions.split_last();
let location = Location { parents, interior: prefix };
(location, 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(Location { interior: i, parents: self.parents }),
Err((i, j)) => Err((Location { 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(Location { interior: i, parents: self.parents }),
Err((i, j)) => Err((Location { 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: &Location) -> Option<&Junction> {
if self.parents != prefix.parents {
return None
}
self.interior.match_and_split(&prefix.interior)
}
pub fn starts_with(&self, prefix: &Location) -> 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 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) -> Location {
let mut clone = self.clone();
while let Some(j) = clone.last() {
if matches!(j, Junction::Parachain(_) | Junction::GlobalConsensus(_)) {
return clone
} else {
(clone, _) = clone.split_last_interior();
}
}
Location::new(clone.parents, Junctions::Here)
}
}
impl Reanchorable for Location {
type Error = Self;
fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> {
let inverted_target = context.invert_target(target)?;
self.prepend_with(inverted_target).map_err(|_| ())?;
self.simplify(target.interior());
Ok(())
}
fn reanchored(mut self, target: &Location, context: &InteriorLocation) -> Result<Self, Self> {
match self.reanchor(target, context) {
Ok(()) => Ok(self),
Err(()) => Err(self),
}
}
}
impl TryFrom<OldLocation> for Option<Location> {
type Error = ();
fn try_from(value: OldLocation) -> result::Result<Self, Self::Error> {
Ok(Some(Location::try_from(value)?))
}
}
impl TryFrom<OldLocation> for Location {
type Error = ();
fn try_from(x: OldLocation) -> result::Result<Self, ()> {
Ok(Location { parents: x.parents, interior: x.interior.try_into()? })
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Parent;
impl From<Parent> for Location {
fn from(_: Parent) -> Self {
Location { parents: 1, interior: Junctions::Here }
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct ParentThen(pub Junctions);
impl From<ParentThen> for Location {
fn from(ParentThen(interior): ParentThen) -> Self {
Location { parents: 1, interior }
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Ancestor(pub u8);
impl From<Ancestor> for Location {
fn from(Ancestor(parents): Ancestor) -> Self {
Location { 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 Location {
fn from(AncestorThen(parents, interior): AncestorThen<Interior>) -> Self {
Location { parents, interior: interior.into() }
}
}
impl From<[u8; 32]> for Location {
fn from(bytes: [u8; 32]) -> Self {
let junction: Junction = bytes.into();
junction.into()
}
}
impl From<sp_runtime::AccountId32> for Location {
fn from(id: sp_runtime::AccountId32) -> Self {
Junction::AccountId32 { network: None, id: id.into() }.into()
}
}
xcm_procedural::impl_conversion_functions_for_location_v4!();
#[cfg(test)]
mod tests {
use crate::v4::prelude::*;
use codec::{Decode, Encode};
#[test]
fn conversion_works() {
let x: Location = Parent.into();
assert_eq!(x, Location { parents: 1, interior: Here });
let x: Location = (Parent, Parent, OnlyChild).into();
assert_eq!(x, Location { parents: 2, interior: OnlyChild.into() });
let x: Location = OnlyChild.into();
assert_eq!(x, Location { parents: 0, interior: OnlyChild.into() });
let x: Location = (OnlyChild,).into();
assert_eq!(x, Location { parents: 0, interior: OnlyChild.into() });
}
#[test]
fn simplify_basic_works() {
let mut location: Location =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = [Parachain(1000), PalletInstance(42)].into();
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: Location = (Parent, PalletInstance(42), GeneralIndex(69)).into();
let context = [PalletInstance(42)].into();
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: Location = (Parent, PalletInstance(42), GeneralIndex(69)).into();
let context = [Parachain(1000), PalletInstance(42)].into();
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: Location =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = [OnlyChild, Parachain(1000), PalletInstance(42)].into();
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
}
#[test]
fn simplify_incompatible_location_fails() {
let mut location: Location =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = [Parachain(1000), PalletInstance(42), GeneralIndex(42)].into();
let expected =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: Location =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = [Parachain(1000)].into();
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: Location = (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 = Location {
parents: 1,
interior: [Parachain(42), AccountIndex64 { network: None, index: 23 }].into(),
};
let encoded = m.encode();
assert_eq!(encoded, [1, 2, 0, 168, 2, 0, 92].to_vec());
let decoded = Location::decode(&mut &encoded[..]);
assert_eq!(decoded, Ok(m));
}
#[test]
fn match_and_split_works() {
let m = Location {
parents: 1,
interior: [Parachain(42), AccountIndex64 { network: None, index: 23 }].into(),
};
assert_eq!(m.match_and_split(&Location { parents: 1, interior: Here }), None);
assert_eq!(
m.match_and_split(&Location { parents: 1, interior: [Parachain(42)].into() }),
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 = Location { parents: 1, interior: [Parachain(42)].into() };
assert_eq!(m.append_with([PalletInstance(3), acc]), Ok(()));
assert_eq!(
m,
Location { parents: 1, interior: [Parachain(42), PalletInstance(3), acc].into() }
);
let acc = AccountIndex64 { network: None, index: 23 };
let m = Location {
parents: 254,
interior: [Parachain(42), OnlyChild, OnlyChild, OnlyChild, OnlyChild].into(),
};
let suffix: Location = (PalletInstance(3), acc, OnlyChild, OnlyChild).into();
assert_eq!(m.clone().append_with(suffix.clone()), Err(suffix));
}
#[test]
fn prepend_with_works() {
let mut m = Location {
parents: 1,
interior: [Parachain(42), AccountIndex64 { network: None, index: 23 }].into(),
};
assert_eq!(m.prepend_with(Location { parents: 1, interior: [OnlyChild].into() }), Ok(()));
assert_eq!(
m,
Location {
parents: 1,
interior: [Parachain(42), AccountIndex64 { network: None, index: 23 }].into()
}
);
let mut m = Location { parents: 254, interior: [Parachain(42)].into() };
let prefix = Location { parents: 2, interior: Here };
assert_eq!(m.prepend_with(prefix.clone()), Err(prefix));
let prefix = Location { parents: 1, interior: Here };
assert_eq!(m.prepend_with(prefix.clone()), Ok(()));
assert_eq!(m, Location { parents: 255, interior: [Parachain(42)].into() });
}
#[test]
fn double_ended_ref_iteration_works() {
let m: Junctions = [Parachain(1000), Parachain(3), PalletInstance(5)].into();
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 conversion_from_other_types_works() {
use crate::v3;
fn takes_location<Arg: Into<Location>>(_arg: Arg) {}
takes_location(Parent);
takes_location(Here);
takes_location([Parachain(42)]);
takes_location((Ancestor(255), PalletInstance(8)));
takes_location((Ancestor(5), Parachain(1), PalletInstance(3)));
takes_location((Ancestor(2), Here));
takes_location(AncestorThen(
3,
[Parachain(43), AccountIndex64 { network: None, index: 155 }],
));
takes_location((Parent, AccountId32 { network: None, id: [0; 32] }));
takes_location((Parent, Here));
takes_location(ParentThen([Parachain(75)].into()));
takes_location([Parachain(100), PalletInstance(3)]);
assert_eq!(v3::Location::from(v3::Junctions::Here).try_into(), Ok(Location::here()));
assert_eq!(v3::Location::from(v3::Parent).try_into(), Ok(Location::parent()));
assert_eq!(
v3::Location::from((v3::Parent, v3::Parent, v3::Junction::GeneralIndex(42u128),))
.try_into(),
Ok(Location { parents: 2, interior: [GeneralIndex(42u128)].into() }),
);
}
}