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