1use 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#[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 pub parents: u8,
72 pub interior: Junctions,
74}
75
76pub type Location = MultiLocation;
78
79impl Default for MultiLocation {
80 fn default() -> Self {
81 Self { parents: 0, interior: Junctions::Here }
82 }
83}
84
85pub type InteriorMultiLocation = Junctions;
89
90impl MultiLocation {
91 pub fn new(parents: u8, interior: impl Into<Junctions>) -> MultiLocation {
93 MultiLocation { parents, interior: interior.into() }
94 }
95
96 pub const fn into_versioned(self) -> VersionedLocation {
98 VersionedLocation::V3(self)
99 }
100
101 pub const fn here() -> MultiLocation {
105 MultiLocation { parents: 0, interior: Junctions::Here }
106 }
107
108 pub const fn parent() -> MultiLocation {
110 MultiLocation { parents: 1, interior: Junctions::Here }
111 }
112
113 pub const fn grandparent() -> MultiLocation {
115 MultiLocation { parents: 2, interior: Junctions::Here }
116 }
117
118 pub const fn ancestor(parents: u8) -> MultiLocation {
120 MultiLocation { parents, interior: Junctions::Here }
121 }
122
123 pub const fn is_here(&self) -> bool {
125 self.parents == 0 && self.interior.len() == 0
126 }
127
128 pub fn remove_network_id(&mut self) {
130 self.interior.remove_network_id();
131 }
132
133 pub fn interior(&self) -> &Junctions {
135 &self.interior
136 }
137
138 pub fn interior_mut(&mut self) -> &mut Junctions {
140 &mut self.interior
141 }
142
143 pub const fn parent_count(&self) -> u8 {
145 self.parents
146 }
147
148 pub const fn contains_parents_only(&self, count: u8) -> bool {
151 matches!(self.interior, Junctions::Here) && self.parents == count
152 }
153
154 pub const fn len(&self) -> usize {
156 self.parent_count() as usize + self.interior.len()
157 }
158
159 pub fn first_interior(&self) -> Option<&Junction> {
162 self.interior.first()
163 }
164
165 pub fn last(&self) -> Option<&Junction> {
167 self.interior.last()
168 }
169
170 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 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 pub fn push_interior(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
192 self.interior.push(new)
193 }
194
195 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 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 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 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 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 pub fn dec_parent(&mut self) {
250 self.parents = self.parents.saturating_sub(1);
251 }
252
253 pub fn take_first_interior(&mut self) -> Option<Junction> {
256 self.interior.take_first()
257 }
258
259 pub fn take_last(&mut self) -> Option<Junction> {
262 self.interior.take_last()
263 }
264
265 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 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 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 pub fn prepend_with(&mut self, prefix: impl Into<Self>) -> Result<(), Self> {
339 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 while self.parents > 0 && prefix.take_last().is_some() {
355 self.dec_parent();
356 }
357
358 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 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 pub fn reanchor(
397 &mut self,
398 target: &MultiLocation,
399 context: InteriorMultiLocation,
400 ) -> Result<(), ()> {
401 let inverted_target = context.invert_target(target)?;
405
406 self.prepend_with(inverted_target).map_err(|_| ())?;
409
410 self.simplify(target.interior());
413
414 Ok(())
415 }
416
417 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 pub fn simplify(&mut self, context: &Junctions) {
435 if context.len() < self.parents as usize {
436 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 pub fn chain_location(&self) -> MultiLocation {
453 let mut clone = *self;
454 while let Some(j) = clone.last() {
456 if matches!(j, Junction::Parachain(_) | Junction::GlobalConsensus(_)) {
457 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#[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#[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#[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#[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, 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 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 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 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 let relay_to_local = MultiLocation::new(0, (PalletInstance(42), GeneralIndex(42)));
716 assert_eq!(relay_to_local.chain_location(), MultiLocation::here());
717
718 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 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 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 let para_to_relay = MultiLocation::new(1, (PalletInstance(42), GeneralIndex(42)));
740 assert_eq!(para_to_relay.chain_location(), MultiLocation::parent());
741
742 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 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 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}