// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // 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 . //! XCM `Junctions`/`InteriorLocation` datatype. use super::{Junction, Location, NetworkId}; use alloc::sync::Arc; use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen}; use core::{mem, ops::Range, result}; use scale_info::TypeInfo; /// Maximum number of `Junction`s that a `Junctions` can contain. pub(crate) const MAX_JUNCTIONS: usize = 8; /// Non-parent junctions that can be constructed, up to the length of 8. This specific `Junctions` /// implementation uses a Rust `enum` in order to make pattern matching easier. /// /// Parent junctions cannot be constructed with this type. Refer to `Location` for /// instructions on constructing parent junctions. #[derive( Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo, MaxEncodedLen, serde::Serialize, serde::Deserialize, )] pub enum Junctions { /// The interpreting consensus system. Here, /// A relative path comprising 1 junction. X1(Arc<[Junction; 1]>), /// A relative path comprising 2 junctions. X2(Arc<[Junction; 2]>), /// A relative path comprising 3 junctions. X3(Arc<[Junction; 3]>), /// A relative path comprising 4 junctions. X4(Arc<[Junction; 4]>), /// A relative path comprising 5 junctions. X5(Arc<[Junction; 5]>), /// A relative path comprising 6 junctions. X6(Arc<[Junction; 6]>), /// A relative path comprising 7 junctions. X7(Arc<[Junction; 7]>), /// A relative path comprising 8 junctions. X8(Arc<[Junction; 8]>), } macro_rules! impl_junctions { ($count:expr, $variant:ident) => { impl From<[Junction; $count]> for Junctions { fn from(junctions: [Junction; $count]) -> Self { Self::$variant(Arc::new(junctions)) } } impl PartialEq<[Junction; $count]> for Junctions { fn eq(&self, rhs: &[Junction; $count]) -> bool { self.as_slice() == rhs } } }; } impl_junctions!(1, X1); impl_junctions!(2, X2); impl_junctions!(3, X3); impl_junctions!(4, X4); impl_junctions!(5, X5); impl_junctions!(6, X6); impl_junctions!(7, X7); impl_junctions!(8, X8); pub struct JunctionsIterator { junctions: Junctions, range: Range, } impl Iterator for JunctionsIterator { type Item = Junction; fn next(&mut self) -> Option { self.junctions.at(self.range.next()?).cloned() } } impl DoubleEndedIterator for JunctionsIterator { fn next_back(&mut self) -> Option { self.junctions.at(self.range.next_back()?).cloned() } } pub struct JunctionsRefIterator<'a> { junctions: &'a Junctions, range: Range, } impl<'a> Iterator for JunctionsRefIterator<'a> { type Item = &'a Junction; fn next(&mut self) -> Option<&'a Junction> { self.junctions.at(self.range.next()?) } } impl<'a> DoubleEndedIterator for JunctionsRefIterator<'a> { fn next_back(&mut self) -> Option<&'a Junction> { self.junctions.at(self.range.next_back()?) } } impl<'a> IntoIterator for &'a Junctions { type Item = &'a Junction; type IntoIter = JunctionsRefIterator<'a>; fn into_iter(self) -> Self::IntoIter { JunctionsRefIterator { junctions: self, range: 0..self.len() } } } impl IntoIterator for Junctions { type Item = Junction; type IntoIter = JunctionsIterator; fn into_iter(self) -> Self::IntoIter { JunctionsIterator { range: 0..self.len(), junctions: self } } } impl Junctions { /// Convert `self` into a `Location` containing 0 parents. /// /// Similar to `Into::into`, except that this method can be used in a const evaluation context. pub const fn into_location(self) -> Location { Location { parents: 0, interior: self } } /// Convert `self` into a `Location` containing `n` parents. /// /// Similar to `Self::into_location`, with the added ability to specify the number of parent /// junctions. pub const fn into_exterior(self, n: u8) -> Location { Location { parents: n, interior: self } } /// Casts `self` into a slice containing `Junction`s. pub fn as_slice(&self) -> &[Junction] { match self { Junctions::Here => &[], Junctions::X1(ref a) => &a[..], Junctions::X2(ref a) => &a[..], Junctions::X3(ref a) => &a[..], Junctions::X4(ref a) => &a[..], Junctions::X5(ref a) => &a[..], Junctions::X6(ref a) => &a[..], Junctions::X7(ref a) => &a[..], Junctions::X8(ref a) => &a[..], } } /// Casts `self` into a mutable slice containing `Junction`s. pub fn as_slice_mut(&mut self) -> &mut [Junction] { match self { Junctions::Here => &mut [], Junctions::X1(ref mut a) => &mut Arc::make_mut(a)[..], Junctions::X2(ref mut a) => &mut Arc::make_mut(a)[..], Junctions::X3(ref mut a) => &mut Arc::make_mut(a)[..], Junctions::X4(ref mut a) => &mut Arc::make_mut(a)[..], Junctions::X5(ref mut a) => &mut Arc::make_mut(a)[..], Junctions::X6(ref mut a) => &mut Arc::make_mut(a)[..], Junctions::X7(ref mut a) => &mut Arc::make_mut(a)[..], Junctions::X8(ref mut a) => &mut Arc::make_mut(a)[..], } } /// Remove the `NetworkId` value in any `Junction`s. pub fn remove_network_id(&mut self) { self.for_each_mut(Junction::remove_network_id); } /// Treating `self` as the universal context, return the location of the local consensus system /// from the point of view of the given `target`. pub fn invert_target(&self, target: &Location) -> Result { let mut itself = self.clone(); let mut junctions = Self::Here; for _ in 0..target.parent_count() { junctions = junctions .pushed_front_with(itself.take_last().unwrap_or(Junction::OnlyChild)) .map_err(|_| ())?; } let parents = target.interior().len() as u8; Ok(Location::new(parents, junctions)) } /// Execute a function `f` on every junction. We use this since we cannot implement a mutable /// `Iterator` without unsafe code. pub fn for_each_mut(&mut self, x: impl FnMut(&mut Junction)) { self.as_slice_mut().iter_mut().for_each(x) } /// Extract the network ID treating this value as a universal location. /// /// This will return an `Err` if the first item is not a `GlobalConsensus`, which would indicate /// that this value is not a universal location. pub fn global_consensus(&self) -> Result { if let Some(Junction::GlobalConsensus(network)) = self.first() { Ok(*network) } else { Err(()) } } /// Extract the network ID and the interior consensus location, treating this value as a /// universal location. /// /// This will return an `Err` if the first item is not a `GlobalConsensus`, which would indicate /// that this value is not a universal location. pub fn split_global(self) -> Result<(NetworkId, Junctions), ()> { match self.split_first() { (location, Some(Junction::GlobalConsensus(network))) => Ok((network, location)), _ => return Err(()), } } /// Treat `self` as a universal location and the context of `relative`, returning the universal /// location of relative. /// /// This will return an error if `relative` has as many (or more) parents than there are /// junctions in `self`, implying that relative refers into a different global consensus. pub fn within_global(mut self, relative: Location) -> Result { if self.len() <= relative.parent_count() as usize { return Err(()); } for _ in 0..relative.parent_count() { self.take_last(); } for j in relative.interior() { self.push(*j).map_err(|_| ())?; } Ok(self) } /// Consumes `self` and returns how `viewer` would address it locally. pub fn relative_to(mut self, viewer: &Junctions) -> Location { let mut i = 0; while match (self.first(), viewer.at(i)) { (Some(x), Some(y)) => x == y, _ => false, } { self = self.split_first().0; // NOTE: Cannot overflow as loop can only iterate at most `MAX_JUNCTIONS` times. i += 1; } // AUDIT NOTES: // - above loop ensures that `i <= viewer.len()`. // - `viewer.len()` is at most `MAX_JUNCTIONS`, so won't overflow a `u8`. Location::new((viewer.len() - i) as u8, self) } /// Returns first junction, or `None` if the location is empty. pub fn first(&self) -> Option<&Junction> { self.as_slice().first() } /// Returns last junction, or `None` if the location is empty. pub fn last(&self) -> Option<&Junction> { self.as_slice().last() } /// Splits off the first 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(self) -> (Junctions, Option) { match self { Junctions::Here => (Junctions::Here, None), Junctions::X1(xs) => { let [a] = *xs; (Junctions::Here, Some(a)) }, Junctions::X2(xs) => { let [a, b] = *xs; ([b].into(), Some(a)) }, Junctions::X3(xs) => { let [a, b, c] = *xs; ([b, c].into(), Some(a)) }, Junctions::X4(xs) => { let [a, b, c, d] = *xs; ([b, c, d].into(), Some(a)) }, Junctions::X5(xs) => { let [a, b, c, d, e] = *xs; ([b, c, d, e].into(), Some(a)) }, Junctions::X6(xs) => { let [a, b, c, d, e, f] = *xs; ([b, c, d, e, f].into(), Some(a)) }, Junctions::X7(xs) => { let [a, b, c, d, e, f, g] = *xs; ([b, c, d, e, f, g].into(), Some(a)) }, Junctions::X8(xs) => { let [a, b, c, d, e, f, g, h] = *xs; ([b, c, d, e, f, g, h].into(), Some(a)) }, } } /// Splits off the last junction, returning the remaining prefix (first item in tuple) and the /// last element (second item in tuple) or `None` if it was empty. pub fn split_last(self) -> (Junctions, Option) { match self { Junctions::Here => (Junctions::Here, None), Junctions::X1(xs) => { let [a] = *xs; (Junctions::Here, Some(a)) }, Junctions::X2(xs) => { let [a, b] = *xs; ([a].into(), Some(b)) }, Junctions::X3(xs) => { let [a, b, c] = *xs; ([a, b].into(), Some(c)) }, Junctions::X4(xs) => { let [a, b, c, d] = *xs; ([a, b, c].into(), Some(d)) }, Junctions::X5(xs) => { let [a, b, c, d, e] = *xs; ([a, b, c, d].into(), Some(e)) }, Junctions::X6(xs) => { let [a, b, c, d, e, f] = *xs; ([a, b, c, d, e].into(), Some(f)) }, Junctions::X7(xs) => { let [a, b, c, d, e, f, g] = *xs; ([a, b, c, d, e, f].into(), Some(g)) }, Junctions::X8(xs) => { let [a, b, c, d, e, f, g, h] = *xs; ([a, b, c, d, e, f, g].into(), Some(h)) }, } } /// Removes the first element from `self`, returning it (or `None` if it was empty). pub fn take_first(&mut self) -> Option { let mut d = Junctions::Here; mem::swap(&mut *self, &mut d); let (tail, head) = d.split_first(); *self = tail; head } /// Removes the last element from `self`, returning it (or `None` if it was empty). pub fn take_last(&mut self) -> Option { let mut d = Junctions::Here; mem::swap(&mut *self, &mut d); let (head, tail) = d.split_last(); *self = head; tail } /// Mutates `self` to be appended with `new` or returns an `Err` with `new` if would overflow. pub fn push(&mut self, new: impl Into) -> 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) }, } } /// Mutates `self` to be prepended with `new` or returns an `Err` with `new` if would overflow. pub fn push_front(&mut self, new: impl Into) -> 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) }, } } /// Consumes `self` and returns a `Junctions` suffixed with `new`, or an `Err` with the /// original value of `self` and `new` in case of overflow. pub fn pushed_with(self, new: impl Into) -> result::Result { let new = new.into(); Ok(match self { Junctions::Here => [new].into(), Junctions::X1(xs) => { let [a] = *xs; [a, new].into() }, Junctions::X2(xs) => { let [a, b] = *xs; [a, b, new].into() }, Junctions::X3(xs) => { let [a, b, c] = *xs; [a, b, c, new].into() }, Junctions::X4(xs) => { let [a, b, c, d] = *xs; [a, b, c, d, new].into() }, Junctions::X5(xs) => { let [a, b, c, d, e] = *xs; [a, b, c, d, e, new].into() }, Junctions::X6(xs) => { let [a, b, c, d, e, f] = *xs; [a, b, c, d, e, f, new].into() }, Junctions::X7(xs) => { let [a, b, c, d, e, f, g] = *xs; [a, b, c, d, e, f, g, new].into() }, s => Err((s, new))?, }) } /// Consumes `self` and returns a `Junctions` prefixed with `new`, or an `Err` with the /// original value of `self` and `new` in case of overflow. pub fn pushed_front_with( self, new: impl Into, ) -> result::Result { let new = new.into(); Ok(match self { Junctions::Here => [new].into(), Junctions::X1(xs) => { let [a] = *xs; [new, a].into() }, Junctions::X2(xs) => { let [a, b] = *xs; [new, a, b].into() }, Junctions::X3(xs) => { let [a, b, c] = *xs; [new, a, b, c].into() }, Junctions::X4(xs) => { let [a, b, c, d] = *xs; [new, a, b, c, d].into() }, Junctions::X5(xs) => { let [a, b, c, d, e] = *xs; [new, a, b, c, d, e].into() }, Junctions::X6(xs) => { let [a, b, c, d, e, f] = *xs; [new, a, b, c, d, e, f].into() }, Junctions::X7(xs) => { let [a, b, c, d, e, f, g] = *xs; [new, a, b, c, d, e, f, g].into() }, s => Err((s, new))?, }) } /// 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 pezstaging_xcm::v5::{Junctions, Junction::*, Location}; /// # fn main() { /// let mut m = Junctions::from([Teyrchain(21)]); /// assert_eq!(m.append_with([PalletInstance(3)]), Ok(())); /// assert_eq!(m, [Teyrchain(21), PalletInstance(3)]); /// # } /// ``` pub fn append_with(&mut self, suffix: impl Into) -> 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(()) } /// Returns the number of junctions in `self`. pub fn len(&self) -> usize { self.as_slice().len() } /// Returns the junction at index `i`, or `None` if the location doesn't contain that many /// elements. pub fn at(&self, i: usize) -> Option<&Junction> { self.as_slice().get(i) } /// Returns a mutable reference to the junction at index `i`, or `None` if the location doesn't /// contain that many elements. pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> { self.as_slice_mut().get_mut(i) } /// Returns a reference iterator over the junctions. pub fn iter(&self) -> JunctionsRefIterator<'_> { JunctionsRefIterator { junctions: self, range: 0..self.len() } } /// 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 pezstaging_xcm::v5::{Junctions, Junction::*}; /// # fn main() { /// let mut m = Junctions::from([Teyrchain(2), PalletInstance(3), OnlyChild]); /// assert_eq!(m.match_and_split(&[Teyrchain(2), PalletInstance(3)].into()), Some(&OnlyChild)); /// assert_eq!(m.match_and_split(&[Teyrchain(2)].into()), None); /// # } /// ``` 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 for Junctions { type Error = Location; fn try_from(x: Location) -> result::Result { if x.parent_count() > 0 { Err(x) } else { Ok(x.interior().clone()) } } } impl> From for Junctions { fn from(x: T) -> Self { [x.into()].into() } } impl From<[Junction; 0]> for Junctions { fn from(_: [Junction; 0]) -> Self { Self::Here } } impl From<()> for Junctions { fn from(_: ()) -> Self { Self::Here } } xcm_pez_procedural::impl_conversion_functions_for_junctions_v5!(); #[cfg(test)] mod tests { use super::{super::prelude::*, *}; #[test] fn inverting_works() { let context: InteriorLocation = (Teyrchain(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: InteriorLocation = (Teyrchain(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 NetworkId::*; assert_eq!( Junctions::from([Pezkuwi.into()]).relative_to(&Junctions::from([Kusama.into()])), (Parent, Pezkuwi).into() ); let base = Junctions::from([Kusama.into(), Teyrchain(1), PalletInstance(1)]); // Ancestors. assert_eq!(Here.relative_to(&base), (Parent, Parent, Parent).into()); assert_eq!(Junctions::from([Kusama.into()]).relative_to(&base), (Parent, Parent).into()); assert_eq!( Junctions::from([Kusama.into(), Teyrchain(1)]).relative_to(&base), (Parent,).into() ); assert_eq!( Junctions::from([Kusama.into(), Teyrchain(1), PalletInstance(1)]).relative_to(&base), Here.into() ); // Ancestors with one child. assert_eq!( Junctions::from([Pezkuwi.into()]).relative_to(&base), (Parent, Parent, Parent, Pezkuwi).into() ); assert_eq!( Junctions::from([Kusama.into(), Teyrchain(2)]).relative_to(&base), (Parent, Parent, Teyrchain(2)).into() ); assert_eq!( Junctions::from([Kusama.into(), Teyrchain(1), PalletInstance(2)]).relative_to(&base), (Parent, PalletInstance(2)).into() ); assert_eq!( Junctions::from([Kusama.into(), Teyrchain(1), PalletInstance(1), [1u8; 32].into()]) .relative_to(&base), ([1u8; 32],).into() ); // Ancestors with grandchildren. assert_eq!( Junctions::from([Pezkuwi.into(), Teyrchain(1)]).relative_to(&base), (Parent, Parent, Parent, Pezkuwi, Teyrchain(1)).into() ); assert_eq!( Junctions::from([Kusama.into(), Teyrchain(2), PalletInstance(1)]).relative_to(&base), (Parent, Parent, Teyrchain(2), PalletInstance(1)).into() ); assert_eq!( Junctions::from([Kusama.into(), Teyrchain(1), PalletInstance(2), [1u8; 32].into()]) .relative_to(&base), (Parent, PalletInstance(2), [1u8; 32]).into() ); assert_eq!( Junctions::from([ Kusama.into(), Teyrchain(1), PalletInstance(1), [1u8; 32].into(), 1u128.into() ]) .relative_to(&base), ([1u8; 32], 1u128).into() ); } #[test] fn global_consensus_works() { use NetworkId::*; assert_eq!(Junctions::from([Pezkuwi.into()]).global_consensus(), Ok(Pezkuwi)); assert_eq!(Junctions::from([Kusama.into(), 1u64.into()]).global_consensus(), Ok(Kusama)); assert_eq!(Here.global_consensus(), Err(())); assert_eq!(Junctions::from([1u64.into()]).global_consensus(), Err(())); assert_eq!(Junctions::from([1u64.into(), Kusama.into()]).global_consensus(), Err(())); } #[test] fn test_conversion() { use super::{Junction::*, NetworkId::*}; let x: Junctions = GlobalConsensus(Pezkuwi).into(); assert_eq!(x, Junctions::from([GlobalConsensus(Pezkuwi)])); let x: Junctions = Pezkuwi.into(); assert_eq!(x, Junctions::from([GlobalConsensus(Pezkuwi)])); let x: Junctions = (Pezkuwi, Kusama).into(); assert_eq!(x, Junctions::from([GlobalConsensus(Pezkuwi), GlobalConsensus(Kusama)])); } #[test] fn encode_decode_junctions_works() { let original = Junctions::from([ Pezkuwi.into(), Kusama.into(), 1u64.into(), GlobalConsensus(Pezkuwi), Teyrchain(123), PalletInstance(45), ]); let encoded = original.encode(); assert_eq!(encoded, &[6, 9, 2, 9, 3, 2, 0, 4, 9, 2, 0, 237, 1, 4, 45]); let decoded = Junctions::decode(&mut &encoded[..]).unwrap(); assert_eq!(decoded, original); } }