referrerpolicy=no-referrer-when-downgrade

staging_xcm/v3/
multilocation.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! XCM `MultiLocation` datatype.
18
19use super::{Junction, Junctions};
20use crate::{v4::Location as NewMultiLocation, VersionedLocation};
21use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
22use core::result;
23use scale_info::TypeInfo;
24
25/// A relative path between state-bearing consensus systems.
26///
27/// A location in a consensus system is defined as an *isolatable state machine* held within global
28/// consensus. The location in question need not have a sophisticated consensus algorithm of its
29/// own; a single account within Ethereum, for example, could be considered a location.
30///
31/// A very-much non-exhaustive list of types of location include:
32/// - A (normal, layer-1) block chain, e.g. the Bitcoin mainnet or a parachain.
33/// - A layer-0 super-chain, e.g. the Polkadot Relay chain.
34/// - A layer-2 smart contract, e.g. an ERC-20 on Ethereum.
35/// - A logical functional component of a chain, e.g. a single instance of a pallet on a Frame-based
36///   Substrate chain.
37/// - An account.
38///
39/// A `MultiLocation` is a *relative identifier*, meaning that it can only be used to define the
40/// relative path between two locations, and cannot generally be used to refer to a location
41/// universally. It is comprised of an integer number of parents specifying the number of times to
42/// "escape" upwards into the containing consensus system and then a number of *junctions*, each
43/// diving down and specifying some interior portion of state (which may be considered a
44/// "sub-consensus" system).
45///
46/// This specific `MultiLocation` implementation uses a `Junctions` datatype which is a Rust `enum`
47/// in order to make pattern matching easier. There are occasions where it is important to ensure
48/// that a value is strictly an interior location, in those cases, `Junctions` may be used.
49///
50/// The `MultiLocation` value of `Null` simply refers to the interpreting consensus system.
51#[derive(
52	Copy,
53	Clone,
54	Decode,
55	Encode,
56	DecodeWithMemTracking,
57	Eq,
58	PartialEq,
59	Ord,
60	PartialOrd,
61	Debug,
62	TypeInfo,
63	MaxEncodedLen,
64	serde::Serialize,
65	serde::Deserialize,
66)]
67#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
68pub struct MultiLocation {
69	/// The number of parent junctions at the beginning of this `MultiLocation`.
70	pub parents: u8,
71	/// The interior (i.e. non-parent) junctions that this `MultiLocation` contains.
72	pub interior: Junctions,
73}
74
75/// Type alias for a better transition to V4.
76pub type Location = MultiLocation;
77
78impl Default for MultiLocation {
79	fn default() -> Self {
80		Self { parents: 0, interior: Junctions::Here }
81	}
82}
83
84/// A relative location which is constrained to be an interior location of the context.
85///
86/// See also `MultiLocation`.
87pub type InteriorMultiLocation = Junctions;
88
89impl MultiLocation {
90	/// Creates a new `MultiLocation` with the given number of parents and interior junctions.
91	pub fn new(parents: u8, interior: impl Into<Junctions>) -> MultiLocation {
92		MultiLocation { parents, interior: interior.into() }
93	}
94
95	/// Consume `self` and return the equivalent `VersionedLocation` value.
96	pub const fn into_versioned(self) -> VersionedLocation {
97		VersionedLocation::V3(self)
98	}
99
100	/// Creates a new `MultiLocation` with 0 parents and a `Here` interior.
101	///
102	/// The resulting `MultiLocation` can be interpreted as the "current consensus system".
103	pub const fn here() -> MultiLocation {
104		MultiLocation { parents: 0, interior: Junctions::Here }
105	}
106
107	/// Creates a new `MultiLocation` which evaluates to the parent context.
108	pub const fn parent() -> MultiLocation {
109		MultiLocation { parents: 1, interior: Junctions::Here }
110	}
111
112	/// Creates a new `MultiLocation` which evaluates to the grand parent context.
113	pub const fn grandparent() -> MultiLocation {
114		MultiLocation { parents: 2, interior: Junctions::Here }
115	}
116
117	/// Creates a new `MultiLocation` with `parents` and an empty (`Here`) interior.
118	pub const fn ancestor(parents: u8) -> MultiLocation {
119		MultiLocation { parents, interior: Junctions::Here }
120	}
121
122	/// Whether the `MultiLocation` has no parents and has a `Here` interior.
123	pub const fn is_here(&self) -> bool {
124		self.parents == 0 && self.interior.len() == 0
125	}
126
127	/// Remove the `NetworkId` value in any interior `Junction`s.
128	pub fn remove_network_id(&mut self) {
129		self.interior.remove_network_id();
130	}
131
132	/// Return a reference to the interior field.
133	pub fn interior(&self) -> &Junctions {
134		&self.interior
135	}
136
137	/// Return a mutable reference to the interior field.
138	pub fn interior_mut(&mut self) -> &mut Junctions {
139		&mut self.interior
140	}
141
142	/// Returns the number of `Parent` junctions at the beginning of `self`.
143	pub const fn parent_count(&self) -> u8 {
144		self.parents
145	}
146
147	/// Returns boolean indicating whether `self` contains only the specified amount of
148	/// parents and no interior junctions.
149	pub const fn contains_parents_only(&self, count: u8) -> bool {
150		matches!(self.interior, Junctions::Here) && self.parents == count
151	}
152
153	/// Returns the number of parents and junctions in `self`.
154	pub const fn len(&self) -> usize {
155		self.parent_count() as usize + self.interior.len()
156	}
157
158	/// Returns the first interior junction, or `None` if the location is empty or contains only
159	/// parents.
160	pub fn first_interior(&self) -> Option<&Junction> {
161		self.interior.first()
162	}
163
164	/// Returns last junction, or `None` if the location is empty or contains only parents.
165	pub fn last(&self) -> Option<&Junction> {
166		self.interior.last()
167	}
168
169	/// Splits off the first interior junction, returning the remaining suffix (first item in tuple)
170	/// and the first element (second item in tuple) or `None` if it was empty.
171	pub fn split_first_interior(self) -> (MultiLocation, Option<Junction>) {
172		let MultiLocation { parents, interior: junctions } = self;
173		let (suffix, first) = junctions.split_first();
174		let multilocation = MultiLocation { parents, interior: suffix };
175		(multilocation, first)
176	}
177
178	/// Splits off the last interior junction, returning the remaining prefix (first item in tuple)
179	/// and the last element (second item in tuple) or `None` if it was empty or if `self` only
180	/// contains parents.
181	pub fn split_last_interior(self) -> (MultiLocation, Option<Junction>) {
182		let MultiLocation { parents, interior: junctions } = self;
183		let (prefix, last) = junctions.split_last();
184		let multilocation = MultiLocation { parents, interior: prefix };
185		(multilocation, last)
186	}
187
188	/// Mutates `self`, suffixing its interior junctions with `new`. Returns `Err` with `new` in
189	/// case of overflow.
190	pub fn push_interior(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
191		self.interior.push(new)
192	}
193
194	/// Mutates `self`, prefixing its interior junctions with `new`. Returns `Err` with `new` in
195	/// case of overflow.
196	pub fn push_front_interior(
197		&mut self,
198		new: impl Into<Junction>,
199	) -> result::Result<(), Junction> {
200		self.interior.push_front(new)
201	}
202
203	/// Consumes `self` and returns a `MultiLocation` suffixed with `new`, or an `Err` with
204	/// the original value of `self` in case of overflow.
205	pub fn pushed_with_interior(
206		self,
207		new: impl Into<Junction>,
208	) -> result::Result<Self, (Self, Junction)> {
209		match self.interior.pushed_with(new) {
210			Ok(i) => Ok(MultiLocation { interior: i, parents: self.parents }),
211			Err((i, j)) => Err((MultiLocation { interior: i, parents: self.parents }, j)),
212		}
213	}
214
215	/// Consumes `self` and returns a `MultiLocation` prefixed with `new`, or an `Err` with the
216	/// original value of `self` in case of overflow.
217	pub fn pushed_front_with_interior(
218		self,
219		new: impl Into<Junction>,
220	) -> result::Result<Self, (Self, Junction)> {
221		match self.interior.pushed_front_with(new) {
222			Ok(i) => Ok(MultiLocation { interior: i, parents: self.parents }),
223			Err((i, j)) => Err((MultiLocation { interior: i, parents: self.parents }, j)),
224		}
225	}
226
227	/// Returns the junction at index `i`, or `None` if the location is a parent or if the location
228	/// does not contain that many elements.
229	pub fn at(&self, i: usize) -> Option<&Junction> {
230		let num_parents = self.parents as usize;
231		if i < num_parents {
232			return None
233		}
234		self.interior.at(i - num_parents)
235	}
236
237	/// Returns a mutable reference to the junction at index `i`, or `None` if the location is a
238	/// parent or if it doesn't contain that many elements.
239	pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> {
240		let num_parents = self.parents as usize;
241		if i < num_parents {
242			return None
243		}
244		self.interior.at_mut(i - num_parents)
245	}
246
247	/// Decrements the parent count by 1.
248	pub fn dec_parent(&mut self) {
249		self.parents = self.parents.saturating_sub(1);
250	}
251
252	/// Removes the first interior junction from `self`, returning it
253	/// (or `None` if it was empty or if `self` contains only parents).
254	pub fn take_first_interior(&mut self) -> Option<Junction> {
255		self.interior.take_first()
256	}
257
258	/// Removes the last element from `interior`, returning it (or `None` if it was empty or if
259	/// `self` only contains parents).
260	pub fn take_last(&mut self) -> Option<Junction> {
261		self.interior.take_last()
262	}
263
264	/// Ensures that `self` has the same number of parents as `prefix`, its junctions begins with
265	/// the junctions of `prefix` and that it has a single `Junction` item following.
266	/// If so, returns a reference to this `Junction` item.
267	///
268	/// # Example
269	/// ```rust
270	/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation};
271	/// let mut m = MultiLocation::new(1, X2(PalletInstance(3), OnlyChild));
272	/// assert_eq!(
273	///     m.match_and_split(&MultiLocation::new(1, X1(PalletInstance(3)))),
274	///     Some(&OnlyChild),
275	/// );
276	/// assert_eq!(m.match_and_split(&MultiLocation::new(1, Here)), None);
277	/// ```
278	pub fn match_and_split(&self, prefix: &MultiLocation) -> Option<&Junction> {
279		if self.parents != prefix.parents {
280			return None
281		}
282		self.interior.match_and_split(&prefix.interior)
283	}
284
285	pub fn starts_with(&self, prefix: &MultiLocation) -> bool {
286		self.parents == prefix.parents && self.interior.starts_with(&prefix.interior)
287	}
288
289	/// Mutate `self` so that it is suffixed with `suffix`.
290	///
291	/// Does not modify `self` and returns `Err` with `suffix` in case of overflow.
292	///
293	/// # Example
294	/// ```rust
295	/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
296	/// let mut m: MultiLocation = (Parent, Parachain(21), 69u64).into();
297	/// assert_eq!(m.append_with((Parent, PalletInstance(3))), Ok(()));
298	/// assert_eq!(m, MultiLocation::new(1, X2(Parachain(21), PalletInstance(3))));
299	/// ```
300	pub fn append_with(&mut self, suffix: impl Into<Self>) -> Result<(), Self> {
301		let prefix = core::mem::replace(self, suffix.into());
302		match self.prepend_with(prefix) {
303			Ok(()) => Ok(()),
304			Err(prefix) => Err(core::mem::replace(self, prefix)),
305		}
306	}
307
308	/// Consume `self` and return its value suffixed with `suffix`.
309	///
310	/// Returns `Err` with the original value of `self` and `suffix` in case of overflow.
311	///
312	/// # Example
313	/// ```rust
314	/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
315	/// let mut m: MultiLocation = (Parent, Parachain(21), 69u64).into();
316	/// let r = m.appended_with((Parent, PalletInstance(3))).unwrap();
317	/// assert_eq!(r, MultiLocation::new(1, X2(Parachain(21), PalletInstance(3))));
318	/// ```
319	pub fn appended_with(mut self, suffix: impl Into<Self>) -> Result<Self, (Self, Self)> {
320		match self.append_with(suffix) {
321			Ok(()) => Ok(self),
322			Err(suffix) => Err((self, suffix)),
323		}
324	}
325
326	/// Mutate `self` so that it is prefixed with `prefix`.
327	///
328	/// Does not modify `self` and returns `Err` with `prefix` in case of overflow.
329	///
330	/// # Example
331	/// ```rust
332	/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
333	/// let mut m: MultiLocation = (Parent, Parent, PalletInstance(3)).into();
334	/// assert_eq!(m.prepend_with((Parent, Parachain(21), OnlyChild)), Ok(()));
335	/// assert_eq!(m, MultiLocation::new(1, X1(PalletInstance(3))));
336	/// ```
337	pub fn prepend_with(&mut self, prefix: impl Into<Self>) -> Result<(), Self> {
338		//     prefix     self (suffix)
339		// P .. P I .. I  p .. p i .. i
340		let mut prefix = prefix.into();
341		let prepend_interior = prefix.interior.len().saturating_sub(self.parents as usize);
342		let final_interior = self.interior.len().saturating_add(prepend_interior);
343		if final_interior > super::junctions::MAX_JUNCTIONS {
344			return Err(prefix)
345		}
346		let suffix_parents = (self.parents as usize).saturating_sub(prefix.interior.len());
347		let final_parents = (prefix.parents as usize).saturating_add(suffix_parents);
348		if final_parents > 255 {
349			return Err(prefix)
350		}
351
352		// cancel out the final item on the prefix interior for one of the suffix's parents.
353		while self.parents > 0 && prefix.take_last().is_some() {
354			self.dec_parent();
355		}
356
357		// now we have either removed all suffix's parents or prefix interior.
358		// this means we can combine the prefix's and suffix's remaining parents/interior since
359		// we know that with at least one empty, the overall order will be respected:
360		//     prefix     self (suffix)
361		// P .. P   (I)   p .. p i .. i => P + p .. (no I) i
362		//  -- or --
363		// P .. P I .. I    (p)  i .. i => P (no p) .. I + i
364
365		self.parents = self.parents.saturating_add(prefix.parents);
366		for j in prefix.interior.into_iter().rev() {
367			self.push_front_interior(j)
368				.expect("final_interior no greater than MAX_JUNCTIONS; qed");
369		}
370		Ok(())
371	}
372
373	/// Consume `self` and return its value prefixed with `prefix`.
374	///
375	/// Returns `Err` with the original value of `self` and `prefix` in case of overflow.
376	///
377	/// # Example
378	/// ```rust
379	/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
380	/// let m: MultiLocation = (Parent, Parent, PalletInstance(3)).into();
381	/// let r = m.prepended_with((Parent, Parachain(21), OnlyChild)).unwrap();
382	/// assert_eq!(r, MultiLocation::new(1, X1(PalletInstance(3))));
383	/// ```
384	pub fn prepended_with(mut self, prefix: impl Into<Self>) -> Result<Self, (Self, Self)> {
385		match self.prepend_with(prefix) {
386			Ok(()) => Ok(self),
387			Err(prefix) => Err((self, prefix)),
388		}
389	}
390
391	/// Mutate `self` so that it represents the same location from the point of view of `target`.
392	/// The context of `self` is provided as `context`.
393	///
394	/// Does not modify `self` in case of overflow.
395	pub fn reanchor(
396		&mut self,
397		target: &MultiLocation,
398		context: InteriorMultiLocation,
399	) -> Result<(), ()> {
400		// TODO: https://github.com/paritytech/polkadot/issues/4489 Optimize this.
401
402		// 1. Use our `context` to figure out how the `target` would address us.
403		let inverted_target = context.invert_target(target)?;
404
405		// 2. Prepend `inverted_target` to `self` to get self's location from the perspective of
406		// `target`.
407		self.prepend_with(inverted_target).map_err(|_| ())?;
408
409		// 3. Given that we know some of `target` context, ensure that any parents in `self` are
410		// strictly needed.
411		self.simplify(target.interior());
412
413		Ok(())
414	}
415
416	/// Consume `self` and return a new value representing the same location from the point of view
417	/// of `target`. The context of `self` is provided as `context`.
418	///
419	/// Returns the original `self` in case of overflow.
420	pub fn reanchored(
421		mut self,
422		target: &MultiLocation,
423		context: InteriorMultiLocation,
424	) -> Result<Self, Self> {
425		match self.reanchor(target, context) {
426			Ok(()) => Ok(self),
427			Err(()) => Err(self),
428		}
429	}
430
431	/// Remove any unneeded parents/junctions in `self` based on the given context it will be
432	/// interpreted in.
433	pub fn simplify(&mut self, context: &Junctions) {
434		if context.len() < self.parents as usize {
435			// Not enough context
436			return
437		}
438		while self.parents > 0 {
439			let maybe = context.at(context.len() - (self.parents as usize));
440			match (self.interior.first(), maybe) {
441				(Some(i), Some(j)) if i == j => {
442					self.interior.take_first();
443					self.parents -= 1;
444				},
445				_ => break,
446			}
447		}
448	}
449
450	/// Return the MultiLocation subsection identifying the chain that `self` points to.
451	pub fn chain_location(&self) -> MultiLocation {
452		let mut clone = *self;
453		// start popping junctions until we reach chain identifier
454		while let Some(j) = clone.last() {
455			if matches!(j, Junction::Parachain(_) | Junction::GlobalConsensus(_)) {
456				// return chain subsection
457				return clone
458			} else {
459				(clone, _) = clone.split_last_interior();
460			}
461		}
462		MultiLocation::new(clone.parents, Junctions::Here)
463	}
464}
465
466impl TryFrom<NewMultiLocation> for Option<MultiLocation> {
467	type Error = ();
468	fn try_from(new: NewMultiLocation) -> result::Result<Self, Self::Error> {
469		Ok(Some(MultiLocation::try_from(new)?))
470	}
471}
472
473impl TryFrom<NewMultiLocation> for MultiLocation {
474	type Error = ();
475	fn try_from(new: NewMultiLocation) -> result::Result<Self, ()> {
476		Ok(MultiLocation {
477			parents: new.parent_count(),
478			interior: new.interior().clone().try_into()?,
479		})
480	}
481}
482
483/// A unit struct which can be converted into a `MultiLocation` of `parents` value 1.
484#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
485pub struct Parent;
486impl From<Parent> for MultiLocation {
487	fn from(_: Parent) -> Self {
488		MultiLocation { parents: 1, interior: Junctions::Here }
489	}
490}
491
492/// A tuple struct which can be converted into a `MultiLocation` of `parents` value 1 with the inner
493/// interior.
494#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
495pub struct ParentThen(pub Junctions);
496impl From<ParentThen> for MultiLocation {
497	fn from(ParentThen(interior): ParentThen) -> Self {
498		MultiLocation { parents: 1, interior }
499	}
500}
501
502/// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value.
503#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
504pub struct Ancestor(pub u8);
505impl From<Ancestor> for MultiLocation {
506	fn from(Ancestor(parents): Ancestor) -> Self {
507		MultiLocation { parents, interior: Junctions::Here }
508	}
509}
510
511/// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value and the
512/// inner interior.
513#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
514pub struct AncestorThen<Interior>(pub u8, pub Interior);
515impl<Interior: Into<Junctions>> From<AncestorThen<Interior>> for MultiLocation {
516	fn from(AncestorThen(parents, interior): AncestorThen<Interior>) -> Self {
517		MultiLocation { parents, interior: interior.into() }
518	}
519}
520
521xcm_procedural::impl_conversion_functions_for_multilocation_v3!();
522
523#[cfg(test)]
524mod tests {
525	use crate::v3::prelude::*;
526	use codec::{Decode, Encode};
527
528	#[test]
529	fn conversion_works() {
530		let x: MultiLocation = Parent.into();
531		assert_eq!(x, MultiLocation { parents: 1, interior: Here });
532		//		let x: MultiLocation = (Parent,).into();
533		//		assert_eq!(x, MultiLocation { parents: 1, interior: Here });
534		//		let x: MultiLocation = (Parent, Parent).into();
535		//		assert_eq!(x, MultiLocation { parents: 2, interior: Here });
536		let x: MultiLocation = (Parent, Parent, OnlyChild).into();
537		assert_eq!(x, MultiLocation { parents: 2, interior: OnlyChild.into() });
538		let x: MultiLocation = OnlyChild.into();
539		assert_eq!(x, MultiLocation { parents: 0, interior: OnlyChild.into() });
540		let x: MultiLocation = (OnlyChild,).into();
541		assert_eq!(x, MultiLocation { parents: 0, interior: OnlyChild.into() });
542	}
543
544	#[test]
545	fn simplify_basic_works() {
546		let mut location: MultiLocation =
547			(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
548		let context = X2(Parachain(1000), PalletInstance(42));
549		let expected = GeneralIndex(69).into();
550		location.simplify(&context);
551		assert_eq!(location, expected);
552
553		let mut location: MultiLocation = (Parent, PalletInstance(42), GeneralIndex(69)).into();
554		let context = X1(PalletInstance(42));
555		let expected = GeneralIndex(69).into();
556		location.simplify(&context);
557		assert_eq!(location, expected);
558
559		let mut location: MultiLocation = (Parent, PalletInstance(42), GeneralIndex(69)).into();
560		let context = X2(Parachain(1000), PalletInstance(42));
561		let expected = GeneralIndex(69).into();
562		location.simplify(&context);
563		assert_eq!(location, expected);
564
565		let mut location: MultiLocation =
566			(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
567		let context = X3(OnlyChild, Parachain(1000), PalletInstance(42));
568		let expected = GeneralIndex(69).into();
569		location.simplify(&context);
570		assert_eq!(location, expected);
571	}
572
573	#[test]
574	fn simplify_incompatible_location_fails() {
575		let mut location: MultiLocation =
576			(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
577		let context = X3(Parachain(1000), PalletInstance(42), GeneralIndex(42));
578		let expected =
579			(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
580		location.simplify(&context);
581		assert_eq!(location, expected);
582
583		let mut location: MultiLocation =
584			(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
585		let context = X1(Parachain(1000));
586		let expected =
587			(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
588		location.simplify(&context);
589		assert_eq!(location, expected);
590	}
591
592	#[test]
593	fn reanchor_works() {
594		let mut id: MultiLocation = (Parent, Parachain(1000), GeneralIndex(42)).into();
595		let context = Parachain(2000).into();
596		let target = (Parent, Parachain(1000)).into();
597		let expected = GeneralIndex(42).into();
598		id.reanchor(&target, context).unwrap();
599		assert_eq!(id, expected);
600	}
601
602	#[test]
603	fn encode_and_decode_works() {
604		let m = MultiLocation {
605			parents: 1,
606			interior: X2(Parachain(42), AccountIndex64 { network: None, index: 23 }),
607		};
608		let encoded = m.encode();
609		assert_eq!(encoded, [1, 2, 0, 168, 2, 0, 92].to_vec());
610		let decoded = MultiLocation::decode(&mut &encoded[..]);
611		assert_eq!(decoded, Ok(m));
612	}
613
614	#[test]
615	fn match_and_split_works() {
616		let m = MultiLocation {
617			parents: 1,
618			interior: X2(Parachain(42), AccountIndex64 { network: None, index: 23 }),
619		};
620		assert_eq!(m.match_and_split(&MultiLocation { parents: 1, interior: Here }), None);
621		assert_eq!(
622			m.match_and_split(&MultiLocation { parents: 1, interior: X1(Parachain(42)) }),
623			Some(&AccountIndex64 { network: None, index: 23 })
624		);
625		assert_eq!(m.match_and_split(&m), None);
626	}
627
628	#[test]
629	fn append_with_works() {
630		let acc = AccountIndex64 { network: None, index: 23 };
631		let mut m = MultiLocation { parents: 1, interior: X1(Parachain(42)) };
632		assert_eq!(m.append_with(X2(PalletInstance(3), acc)), Ok(()));
633		assert_eq!(
634			m,
635			MultiLocation { parents: 1, interior: X3(Parachain(42), PalletInstance(3), acc) }
636		);
637
638		// cannot append to create overly long multilocation
639		let acc = AccountIndex64 { network: None, index: 23 };
640		let m = MultiLocation {
641			parents: 254,
642			interior: X5(Parachain(42), OnlyChild, OnlyChild, OnlyChild, OnlyChild),
643		};
644		let suffix: MultiLocation = (PalletInstance(3), acc, OnlyChild, OnlyChild).into();
645		assert_eq!(m.clone().append_with(suffix), Err(suffix));
646	}
647
648	#[test]
649	fn prepend_with_works() {
650		let mut m = MultiLocation {
651			parents: 1,
652			interior: X2(Parachain(42), AccountIndex64 { network: None, index: 23 }),
653		};
654		assert_eq!(m.prepend_with(MultiLocation { parents: 1, interior: X1(OnlyChild) }), Ok(()));
655		assert_eq!(
656			m,
657			MultiLocation {
658				parents: 1,
659				interior: X2(Parachain(42), AccountIndex64 { network: None, index: 23 })
660			}
661		);
662
663		// cannot prepend to create overly long multilocation
664		let mut m = MultiLocation { parents: 254, interior: X1(Parachain(42)) };
665		let prefix = MultiLocation { parents: 2, interior: Here };
666		assert_eq!(m.prepend_with(prefix), Err(prefix));
667
668		let prefix = MultiLocation { parents: 1, interior: Here };
669		assert_eq!(m.prepend_with(prefix), Ok(()));
670		assert_eq!(m, MultiLocation { parents: 255, interior: X1(Parachain(42)) });
671	}
672
673	#[test]
674	fn double_ended_ref_iteration_works() {
675		let m = X3(Parachain(1000), Parachain(3), PalletInstance(5));
676		let mut iter = m.iter();
677
678		let first = iter.next().unwrap();
679		assert_eq!(first, &Parachain(1000));
680		let third = iter.next_back().unwrap();
681		assert_eq!(third, &PalletInstance(5));
682		let second = iter.next_back().unwrap();
683		assert_eq!(iter.next(), None);
684		assert_eq!(iter.next_back(), None);
685		assert_eq!(second, &Parachain(3));
686
687		let res = Here
688			.pushed_with(*first)
689			.unwrap()
690			.pushed_with(*second)
691			.unwrap()
692			.pushed_with(*third)
693			.unwrap();
694		assert_eq!(m, res);
695
696		// make sure there's no funny business with the 0 indexing
697		let m = Here;
698		let mut iter = m.iter();
699
700		assert_eq!(iter.next(), None);
701		assert_eq!(iter.next_back(), None);
702	}
703
704	#[test]
705	fn chain_location_works() {
706		// Relay-chain or parachain context pointing to local resource,
707		let relay_to_local = MultiLocation::new(0, (PalletInstance(42), GeneralIndex(42)));
708		assert_eq!(relay_to_local.chain_location(), MultiLocation::here());
709
710		// Relay-chain context pointing to child parachain,
711		let relay_to_child =
712			MultiLocation::new(0, (Parachain(42), PalletInstance(42), GeneralIndex(42)));
713		let expected = MultiLocation::new(0, Parachain(42));
714		assert_eq!(relay_to_child.chain_location(), expected);
715
716		// Relay-chain context pointing to different consensus relay,
717		let relay_to_remote_relay =
718			MultiLocation::new(1, (GlobalConsensus(Kusama), PalletInstance(42), GeneralIndex(42)));
719		let expected = MultiLocation::new(1, GlobalConsensus(Kusama));
720		assert_eq!(relay_to_remote_relay.chain_location(), expected);
721
722		// Relay-chain context pointing to different consensus parachain,
723		let relay_to_remote_para = MultiLocation::new(
724			1,
725			(GlobalConsensus(Kusama), Parachain(42), PalletInstance(42), GeneralIndex(42)),
726		);
727		let expected = MultiLocation::new(1, (GlobalConsensus(Kusama), Parachain(42)));
728		assert_eq!(relay_to_remote_para.chain_location(), expected);
729
730		// Parachain context pointing to relay chain,
731		let para_to_relay = MultiLocation::new(1, (PalletInstance(42), GeneralIndex(42)));
732		assert_eq!(para_to_relay.chain_location(), MultiLocation::parent());
733
734		// Parachain context pointing to sibling parachain,
735		let para_to_sibling =
736			MultiLocation::new(1, (Parachain(42), PalletInstance(42), GeneralIndex(42)));
737		let expected = MultiLocation::new(1, Parachain(42));
738		assert_eq!(para_to_sibling.chain_location(), expected);
739
740		// Parachain context pointing to different consensus relay,
741		let para_to_remote_relay =
742			MultiLocation::new(2, (GlobalConsensus(Kusama), PalletInstance(42), GeneralIndex(42)));
743		let expected = MultiLocation::new(2, GlobalConsensus(Kusama));
744		assert_eq!(para_to_remote_relay.chain_location(), expected);
745
746		// Parachain context pointing to different consensus parachain,
747		let para_to_remote_para = MultiLocation::new(
748			2,
749			(GlobalConsensus(Kusama), Parachain(42), PalletInstance(42), GeneralIndex(42)),
750		);
751		let expected = MultiLocation::new(2, (GlobalConsensus(Kusama), Parachain(42)));
752		assert_eq!(para_to_remote_para.chain_location(), expected);
753	}
754}