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

            
19
use super::{Junction, Junctions};
20
use crate::{
21
	v2::MultiLocation as OldMultiLocation, v4::Location as NewMultiLocation, VersionedLocation,
22
};
23
use codec::{Decode, Encode, MaxEncodedLen};
24
use core::result;
25
use 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))]
69
pub 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.
77
pub type Location = MultiLocation;
78

            
79
impl 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`.
88
pub type InteriorMultiLocation = Junctions;
89

            
90
impl 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

            
467
impl TryFrom<OldMultiLocation> for MultiLocation {
468
	type Error = ();
469
10844
	fn try_from(x: OldMultiLocation) -> result::Result<Self, ()> {
470
10844
		Ok(MultiLocation { parents: x.parents, interior: x.interior.try_into()? })
471
10844
	}
472
}
473

            
474
impl 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

            
481
impl 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)]
493
pub struct Parent;
494
impl 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)]
503
pub struct ParentThen(pub Junctions);
504
impl 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)]
512
pub struct Ancestor(pub u8);
513
impl 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)]
522
pub struct AncestorThen<Interior>(pub u8, pub Interior);
523
impl<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

            
529
xcm_procedural::impl_conversion_functions_for_multilocation_v3!();
530

            
531
#[cfg(test)]
532
mod 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
}