use super::{Junction, MultiLocation, NetworkId};
use codec::{Decode, Encode, MaxEncodedLen};
use core::{mem, result};
use scale_info::TypeInfo;
pub(crate) const MAX_JUNCTIONS: usize = 8;
#[derive(
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Encode,
Decode,
Debug,
TypeInfo,
MaxEncodedLen,
serde::Serialize,
serde::Deserialize,
)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
pub enum Junctions {
Here,
X1(Junction),
X2(Junction, Junction),
X3(Junction, Junction, Junction),
X4(Junction, Junction, Junction, Junction),
X5(Junction, Junction, Junction, Junction, Junction),
X6(Junction, Junction, Junction, Junction, Junction, Junction),
X7(Junction, Junction, Junction, Junction, Junction, Junction, Junction),
X8(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction),
}
macro_rules! impl_junction {
($count:expr, $variant:ident, ($($index:literal),+)) => {
impl From<[Junction; $count]> for Junctions {
fn from(junctions: [Junction; $count]) -> Self {
Self::$variant($(junctions[$index]),*)
}
}
};
}
impl_junction!(1, X1, (0));
impl_junction!(2, X2, (0, 1));
impl_junction!(3, X3, (0, 1, 2));
impl_junction!(4, X4, (0, 1, 2, 3));
impl_junction!(5, X5, (0, 1, 2, 3, 4));
impl_junction!(6, X6, (0, 1, 2, 3, 4, 5));
impl_junction!(7, X7, (0, 1, 2, 3, 4, 5, 6));
impl_junction!(8, X8, (0, 1, 2, 3, 4, 5, 6, 7));
pub struct JunctionsIterator(Junctions);
impl Iterator for JunctionsIterator {
type Item = Junction;
fn next(&mut self) -> Option<Junction> {
self.0.take_first()
}
}
impl DoubleEndedIterator for JunctionsIterator {
fn next_back(&mut self) -> Option<Junction> {
self.0.take_last()
}
}
pub struct JunctionsRefIterator<'a> {
junctions: &'a Junctions,
next: usize,
back: usize,
}
impl<'a> Iterator for JunctionsRefIterator<'a> {
type Item = &'a Junction;
fn next(&mut self) -> Option<&'a Junction> {
if self.next.saturating_add(self.back) >= self.junctions.len() {
return None
}
let result = self.junctions.at(self.next);
self.next += 1;
result
}
}
impl<'a> DoubleEndedIterator for JunctionsRefIterator<'a> {
fn next_back(&mut self) -> Option<&'a Junction> {
let next_back = self.back.saturating_add(1);
let index = self.junctions.len().checked_sub(next_back)?;
if self.next > index {
return None
}
self.back = next_back;
self.junctions.at(index)
}
}
impl<'a> IntoIterator for &'a Junctions {
type Item = &'a Junction;
type IntoIter = JunctionsRefIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
JunctionsRefIterator { junctions: self, next: 0, back: 0 }
}
}
impl IntoIterator for Junctions {
type Item = Junction;
type IntoIter = JunctionsIterator;
fn into_iter(self) -> Self::IntoIter {
JunctionsIterator(self)
}
}
impl Junctions {
pub const fn into_location(self) -> MultiLocation {
MultiLocation { parents: 0, interior: self }
}
pub const fn into_exterior(self, n: u8) -> MultiLocation {
MultiLocation { parents: n, interior: self }
}
pub fn remove_network_id(&mut self) {
self.for_each_mut(Junction::remove_network_id);
}
pub fn invert_target(mut self, target: &MultiLocation) -> Result<MultiLocation, ()> {
let mut junctions = Self::Here;
for _ in 0..target.parent_count() {
junctions = junctions
.pushed_front_with(self.take_last().unwrap_or(Junction::OnlyChild))
.map_err(|_| ())?;
}
let parents = target.interior().len() as u8;
Ok(MultiLocation::new(parents, junctions))
}
pub fn for_each_mut(&mut self, mut x: impl FnMut(&mut Junction)) {
match self {
Junctions::Here => {},
Junctions::X1(a) => {
x(a);
},
Junctions::X2(a, b) => {
x(a);
x(b);
},
Junctions::X3(a, b, c) => {
x(a);
x(b);
x(c);
},
Junctions::X4(a, b, c, d) => {
x(a);
x(b);
x(c);
x(d);
},
Junctions::X5(a, b, c, d, e) => {
x(a);
x(b);
x(c);
x(d);
x(e);
},
Junctions::X6(a, b, c, d, e, f) => {
x(a);
x(b);
x(c);
x(d);
x(e);
x(f);
},
Junctions::X7(a, b, c, d, e, f, g) => {
x(a);
x(b);
x(c);
x(d);
x(e);
x(f);
x(g);
},
Junctions::X8(a, b, c, d, e, f, g, h) => {
x(a);
x(b);
x(c);
x(d);
x(e);
x(f);
x(g);
x(h);
},
}
}
pub fn global_consensus(&self) -> Result<NetworkId, ()> {
if let Some(Junction::GlobalConsensus(network)) = self.first() {
Ok(*network)
} else {
Err(())
}
}
pub fn split_global(self) -> Result<(NetworkId, Junctions), ()> {
match self.split_first() {
(location, Some(Junction::GlobalConsensus(network))) => Ok((network, location)),
_ => return Err(()),
}
}
pub fn within_global(mut self, relative: MultiLocation) -> Result<Self, ()> {
if self.len() <= relative.parents as usize {
return Err(())
}
for _ in 0..relative.parents {
self.take_last();
}
for j in relative.interior {
self.push(j).map_err(|_| ())?;
}
Ok(self)
}
pub fn relative_to(mut self, viewer: &Junctions) -> MultiLocation {
let mut i = 0;
while match (self.first(), viewer.at(i)) {
(Some(x), Some(y)) => x == y,
_ => false,
} {
self = self.split_first().0;
i += 1;
}
MultiLocation { parents: (viewer.len() - i) as u8, interior: self }
}
pub fn first(&self) -> Option<&Junction> {
match &self {
Junctions::Here => None,
Junctions::X1(ref a) => Some(a),
Junctions::X2(ref a, ..) => Some(a),
Junctions::X3(ref a, ..) => Some(a),
Junctions::X4(ref a, ..) => Some(a),
Junctions::X5(ref a, ..) => Some(a),
Junctions::X6(ref a, ..) => Some(a),
Junctions::X7(ref a, ..) => Some(a),
Junctions::X8(ref a, ..) => Some(a),
}
}
pub fn last(&self) -> Option<&Junction> {
match &self {
Junctions::Here => None,
Junctions::X1(ref a) => Some(a),
Junctions::X2(.., ref a) => Some(a),
Junctions::X3(.., ref a) => Some(a),
Junctions::X4(.., ref a) => Some(a),
Junctions::X5(.., ref a) => Some(a),
Junctions::X6(.., ref a) => Some(a),
Junctions::X7(.., ref a) => Some(a),
Junctions::X8(.., ref a) => Some(a),
}
}
pub fn split_first(self) -> (Junctions, Option<Junction>) {
match self {
Junctions::Here => (Junctions::Here, None),
Junctions::X1(a) => (Junctions::Here, Some(a)),
Junctions::X2(a, b) => (Junctions::X1(b), Some(a)),
Junctions::X3(a, b, c) => (Junctions::X2(b, c), Some(a)),
Junctions::X4(a, b, c, d) => (Junctions::X3(b, c, d), Some(a)),
Junctions::X5(a, b, c, d, e) => (Junctions::X4(b, c, d, e), Some(a)),
Junctions::X6(a, b, c, d, e, f) => (Junctions::X5(b, c, d, e, f), Some(a)),
Junctions::X7(a, b, c, d, e, f, g) => (Junctions::X6(b, c, d, e, f, g), Some(a)),
Junctions::X8(a, b, c, d, e, f, g, h) => (Junctions::X7(b, c, d, e, f, g, h), Some(a)),
}
}
pub fn split_last(self) -> (Junctions, Option<Junction>) {
match self {
Junctions::Here => (Junctions::Here, None),
Junctions::X1(a) => (Junctions::Here, Some(a)),
Junctions::X2(a, b) => (Junctions::X1(a), Some(b)),
Junctions::X3(a, b, c) => (Junctions::X2(a, b), Some(c)),
Junctions::X4(a, b, c, d) => (Junctions::X3(a, b, c), Some(d)),
Junctions::X5(a, b, c, d, e) => (Junctions::X4(a, b, c, d), Some(e)),
Junctions::X6(a, b, c, d, e, f) => (Junctions::X5(a, b, c, d, e), Some(f)),
Junctions::X7(a, b, c, d, e, f, g) => (Junctions::X6(a, b, c, d, e, f), Some(g)),
Junctions::X8(a, b, c, d, e, f, g, h) => (Junctions::X7(a, b, c, d, e, f, g), Some(h)),
}
}
pub fn take_first(&mut self) -> Option<Junction> {
let mut d = Junctions::Here;
mem::swap(&mut *self, &mut d);
let (tail, head) = d.split_first();
*self = tail;
head
}
pub fn take_last(&mut self) -> Option<Junction> {
let mut d = Junctions::Here;
mem::swap(&mut *self, &mut d);
let (head, tail) = d.split_last();
*self = head;
tail
}
pub fn push(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
let new = new.into();
let mut dummy = Junctions::Here;
mem::swap(self, &mut dummy);
match dummy.pushed_with(new) {
Ok(s) => {
*self = s;
Ok(())
},
Err((s, j)) => {
*self = s;
Err(j)
},
}
}
pub fn push_front(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
let new = new.into();
let mut dummy = Junctions::Here;
mem::swap(self, &mut dummy);
match dummy.pushed_front_with(new) {
Ok(s) => {
*self = s;
Ok(())
},
Err((s, j)) => {
*self = s;
Err(j)
},
}
}
pub fn pushed_with(self, new: impl Into<Junction>) -> result::Result<Self, (Self, Junction)> {
let new = new.into();
Ok(match self {
Junctions::Here => Junctions::X1(new),
Junctions::X1(a) => Junctions::X2(a, new),
Junctions::X2(a, b) => Junctions::X3(a, b, new),
Junctions::X3(a, b, c) => Junctions::X4(a, b, c, new),
Junctions::X4(a, b, c, d) => Junctions::X5(a, b, c, d, new),
Junctions::X5(a, b, c, d, e) => Junctions::X6(a, b, c, d, e, new),
Junctions::X6(a, b, c, d, e, f) => Junctions::X7(a, b, c, d, e, f, new),
Junctions::X7(a, b, c, d, e, f, g) => Junctions::X8(a, b, c, d, e, f, g, new),
s => Err((s, new))?,
})
}
pub fn pushed_front_with(
self,
new: impl Into<Junction>,
) -> result::Result<Self, (Self, Junction)> {
let new = new.into();
Ok(match self {
Junctions::Here => Junctions::X1(new),
Junctions::X1(a) => Junctions::X2(new, a),
Junctions::X2(a, b) => Junctions::X3(new, a, b),
Junctions::X3(a, b, c) => Junctions::X4(new, a, b, c),
Junctions::X4(a, b, c, d) => Junctions::X5(new, a, b, c, d),
Junctions::X5(a, b, c, d, e) => Junctions::X6(new, a, b, c, d, e),
Junctions::X6(a, b, c, d, e, f) => Junctions::X7(new, a, b, c, d, e, f),
Junctions::X7(a, b, c, d, e, f, g) => Junctions::X8(new, a, b, c, d, e, f, g),
s => Err((s, new))?,
})
}
pub fn append_with(&mut self, suffix: impl Into<Junctions>) -> Result<(), Junctions> {
let suffix = suffix.into();
if self.len().saturating_add(suffix.len()) > MAX_JUNCTIONS {
return Err(suffix)
}
for j in suffix.into_iter() {
self.push(j).expect("Already checked the sum of the len()s; qed")
}
Ok(())
}
pub const fn len(&self) -> usize {
match &self {
Junctions::Here => 0,
Junctions::X1(..) => 1,
Junctions::X2(..) => 2,
Junctions::X3(..) => 3,
Junctions::X4(..) => 4,
Junctions::X5(..) => 5,
Junctions::X6(..) => 6,
Junctions::X7(..) => 7,
Junctions::X8(..) => 8,
}
}
pub fn at(&self, i: usize) -> Option<&Junction> {
Some(match (i, self) {
(0, Junctions::X1(ref a)) => a,
(0, Junctions::X2(ref a, ..)) => a,
(0, Junctions::X3(ref a, ..)) => a,
(0, Junctions::X4(ref a, ..)) => a,
(0, Junctions::X5(ref a, ..)) => a,
(0, Junctions::X6(ref a, ..)) => a,
(0, Junctions::X7(ref a, ..)) => a,
(0, Junctions::X8(ref a, ..)) => a,
(1, Junctions::X2(_, ref a)) => a,
(1, Junctions::X3(_, ref a, ..)) => a,
(1, Junctions::X4(_, ref a, ..)) => a,
(1, Junctions::X5(_, ref a, ..)) => a,
(1, Junctions::X6(_, ref a, ..)) => a,
(1, Junctions::X7(_, ref a, ..)) => a,
(1, Junctions::X8(_, ref a, ..)) => a,
(2, Junctions::X3(_, _, ref a)) => a,
(2, Junctions::X4(_, _, ref a, ..)) => a,
(2, Junctions::X5(_, _, ref a, ..)) => a,
(2, Junctions::X6(_, _, ref a, ..)) => a,
(2, Junctions::X7(_, _, ref a, ..)) => a,
(2, Junctions::X8(_, _, ref a, ..)) => a,
(3, Junctions::X4(_, _, _, ref a)) => a,
(3, Junctions::X5(_, _, _, ref a, ..)) => a,
(3, Junctions::X6(_, _, _, ref a, ..)) => a,
(3, Junctions::X7(_, _, _, ref a, ..)) => a,
(3, Junctions::X8(_, _, _, ref a, ..)) => a,
(4, Junctions::X5(_, _, _, _, ref a)) => a,
(4, Junctions::X6(_, _, _, _, ref a, ..)) => a,
(4, Junctions::X7(_, _, _, _, ref a, ..)) => a,
(4, Junctions::X8(_, _, _, _, ref a, ..)) => a,
(5, Junctions::X6(_, _, _, _, _, ref a)) => a,
(5, Junctions::X7(_, _, _, _, _, ref a, ..)) => a,
(5, Junctions::X8(_, _, _, _, _, ref a, ..)) => a,
(6, Junctions::X7(_, _, _, _, _, _, ref a)) => a,
(6, Junctions::X8(_, _, _, _, _, _, ref a, ..)) => a,
(7, Junctions::X8(_, _, _, _, _, _, _, ref a)) => a,
_ => return None,
})
}
pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> {
Some(match (i, self) {
(0, Junctions::X1(ref mut a)) => a,
(0, Junctions::X2(ref mut a, ..)) => a,
(0, Junctions::X3(ref mut a, ..)) => a,
(0, Junctions::X4(ref mut a, ..)) => a,
(0, Junctions::X5(ref mut a, ..)) => a,
(0, Junctions::X6(ref mut a, ..)) => a,
(0, Junctions::X7(ref mut a, ..)) => a,
(0, Junctions::X8(ref mut a, ..)) => a,
(1, Junctions::X2(_, ref mut a)) => a,
(1, Junctions::X3(_, ref mut a, ..)) => a,
(1, Junctions::X4(_, ref mut a, ..)) => a,
(1, Junctions::X5(_, ref mut a, ..)) => a,
(1, Junctions::X6(_, ref mut a, ..)) => a,
(1, Junctions::X7(_, ref mut a, ..)) => a,
(1, Junctions::X8(_, ref mut a, ..)) => a,
(2, Junctions::X3(_, _, ref mut a)) => a,
(2, Junctions::X4(_, _, ref mut a, ..)) => a,
(2, Junctions::X5(_, _, ref mut a, ..)) => a,
(2, Junctions::X6(_, _, ref mut a, ..)) => a,
(2, Junctions::X7(_, _, ref mut a, ..)) => a,
(2, Junctions::X8(_, _, ref mut a, ..)) => a,
(3, Junctions::X4(_, _, _, ref mut a)) => a,
(3, Junctions::X5(_, _, _, ref mut a, ..)) => a,
(3, Junctions::X6(_, _, _, ref mut a, ..)) => a,
(3, Junctions::X7(_, _, _, ref mut a, ..)) => a,
(3, Junctions::X8(_, _, _, ref mut a, ..)) => a,
(4, Junctions::X5(_, _, _, _, ref mut a)) => a,
(4, Junctions::X6(_, _, _, _, ref mut a, ..)) => a,
(4, Junctions::X7(_, _, _, _, ref mut a, ..)) => a,
(4, Junctions::X8(_, _, _, _, ref mut a, ..)) => a,
(5, Junctions::X6(_, _, _, _, _, ref mut a)) => a,
(5, Junctions::X7(_, _, _, _, _, ref mut a, ..)) => a,
(5, Junctions::X8(_, _, _, _, _, ref mut a, ..)) => a,
(6, Junctions::X7(_, _, _, _, _, _, ref mut a)) => a,
(6, Junctions::X8(_, _, _, _, _, _, ref mut a, ..)) => a,
(7, Junctions::X8(_, _, _, _, _, _, _, ref mut a)) => a,
_ => return None,
})
}
pub fn iter(&self) -> JunctionsRefIterator {
JunctionsRefIterator { junctions: self, next: 0, back: 0 }
}
pub fn match_and_split(&self, prefix: &Junctions) -> 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())
}
pub fn starts_with(&self, prefix: &Junctions) -> bool {
prefix.len() <= self.len() && prefix.iter().zip(self.iter()).all(|(x, y)| x == y)
}
}
impl TryFrom<MultiLocation> for Junctions {
type Error = MultiLocation;
fn try_from(x: MultiLocation) -> result::Result<Self, MultiLocation> {
if x.parents > 0 {
Err(x)
} else {
Ok(x.interior)
}
}
}
impl<T: Into<Junction>> From<T> for Junctions {
fn from(x: T) -> Self {
Self::X1(x.into())
}
}
impl From<[Junction; 0]> for Junctions {
fn from(_: [Junction; 0]) -> Self {
Self::Here
}
}
impl From<()> for Junctions {
fn from(_: ()) -> Self {
Self::Here
}
}
xcm_procedural::impl_conversion_functions_for_junctions_v3!();
#[cfg(test)]
mod tests {
use super::{super::prelude::*, *};
#[test]
fn inverting_works() {
let context: InteriorMultiLocation = (Parachain(1000), PalletInstance(42)).into();
let target = (Parent, PalletInstance(69)).into();
let expected = (Parent, PalletInstance(42)).into();
let inverted = context.invert_target(&target).unwrap();
assert_eq!(inverted, expected);
let context: InteriorMultiLocation =
(Parachain(1000), PalletInstance(42), GeneralIndex(1)).into();
let target = (Parent, Parent, PalletInstance(69), GeneralIndex(2)).into();
let expected = (Parent, Parent, PalletInstance(42), GeneralIndex(1)).into();
let inverted = context.invert_target(&target).unwrap();
assert_eq!(inverted, expected);
}
#[test]
fn relative_to_works() {
use Junctions::*;
use NetworkId::*;
assert_eq!(X1(Polkadot.into()).relative_to(&X1(Kusama.into())), (Parent, Polkadot).into());
let base = X3(Kusama.into(), Parachain(1), PalletInstance(1));
assert_eq!(Here.relative_to(&base), (Parent, Parent, Parent).into());
assert_eq!(X1(Kusama.into()).relative_to(&base), (Parent, Parent).into());
assert_eq!(X2(Kusama.into(), Parachain(1)).relative_to(&base), (Parent,).into());
assert_eq!(
X3(Kusama.into(), Parachain(1), PalletInstance(1)).relative_to(&base),
Here.into()
);
assert_eq!(
X1(Polkadot.into()).relative_to(&base),
(Parent, Parent, Parent, Polkadot).into()
);
assert_eq!(
X2(Kusama.into(), Parachain(2)).relative_to(&base),
(Parent, Parent, Parachain(2)).into()
);
assert_eq!(
X3(Kusama.into(), Parachain(1), PalletInstance(2)).relative_to(&base),
(Parent, PalletInstance(2)).into()
);
assert_eq!(
X4(Kusama.into(), Parachain(1), PalletInstance(1), [1u8; 32].into()).relative_to(&base),
([1u8; 32],).into()
);
assert_eq!(
X2(Polkadot.into(), Parachain(1)).relative_to(&base),
(Parent, Parent, Parent, Polkadot, Parachain(1)).into()
);
assert_eq!(
X3(Kusama.into(), Parachain(2), PalletInstance(1)).relative_to(&base),
(Parent, Parent, Parachain(2), PalletInstance(1)).into()
);
assert_eq!(
X4(Kusama.into(), Parachain(1), PalletInstance(2), [1u8; 32].into()).relative_to(&base),
(Parent, PalletInstance(2), [1u8; 32]).into()
);
assert_eq!(
X5(Kusama.into(), Parachain(1), PalletInstance(1), [1u8; 32].into(), 1u128.into())
.relative_to(&base),
([1u8; 32], 1u128).into()
);
}
#[test]
fn global_consensus_works() {
use Junctions::*;
use NetworkId::*;
assert_eq!(X1(Polkadot.into()).global_consensus(), Ok(Polkadot));
assert_eq!(X2(Kusama.into(), 1u64.into()).global_consensus(), Ok(Kusama));
assert_eq!(Here.global_consensus(), Err(()));
assert_eq!(X1(1u64.into()).global_consensus(), Err(()));
assert_eq!(X2(1u64.into(), Kusama.into()).global_consensus(), Err(()));
}
#[test]
fn test_conversion() {
use super::{Junction::*, Junctions::*, NetworkId::*};
let x: Junctions = GlobalConsensus(Polkadot).into();
assert_eq!(x, X1(GlobalConsensus(Polkadot)));
let x: Junctions = Polkadot.into();
assert_eq!(x, X1(GlobalConsensus(Polkadot)));
let x: Junctions = (Polkadot, Kusama).into();
assert_eq!(x, X2(GlobalConsensus(Polkadot), GlobalConsensus(Kusama)));
}
}