Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Add starts_with to v0 and v1 MultiLocation (#6311)
Browse files Browse the repository at this point in the history
* add `starts_with` to v0 and v1 MultiLocation

* add tests

* fmt
  • Loading branch information
joepetrowski authored Nov 20, 2022
1 parent 8065159 commit 8b9f580
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 12 deletions.
43 changes: 37 additions & 6 deletions xcm/src/v0/multi_location.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,17 +356,30 @@ impl MultiLocation {
/// # }
/// ```
pub fn match_and_split(&self, prefix: &MultiLocation) -> Option<&Junction> {
if prefix.len() + 1 != self.len() {
if prefix.len() + 1 != self.len() || !self.starts_with(prefix) {
return None
}
for i in 0..prefix.len() {
if prefix.at(i) != self.at(i) {
return None
}
}
return self.at(prefix.len())
}

/// Returns whether `self` begins with or is equal to `prefix`.
///
/// # Example
/// ```rust
/// # use xcm::v0::{Junction::*, MultiLocation::*};
/// let m = X4(Parent, PalletInstance(3), OnlyChild, OnlyChild);
/// assert!(m.starts_with(&X2(Parent, PalletInstance(3))));
/// assert!(m.starts_with(&m));
/// assert!(!m.starts_with(&X2(Parent, GeneralIndex(99))));
/// assert!(!m.starts_with(&X1(PalletInstance(3))));
/// ```
pub fn starts_with(&self, prefix: &MultiLocation) -> bool {
if self.len() < prefix.len() {
return false
}
prefix.iter().zip(self.iter()).all(|(l, r)| l == r)
}

/// Mutates `self`, suffixing it with `new`. Returns `Err` in case of overflow.
pub fn push(&mut self, new: Junction) -> result::Result<(), ()> {
let mut n = MultiLocation::Null;
Expand Down Expand Up @@ -601,6 +614,24 @@ mod tests {
assert_eq!(m.match_and_split(&m), None);
}

#[test]
fn starts_with_works() {
let full = X3(Parent, Parachain(1000), AccountIndex64 { network: Any, index: 23 });
let identity = full.clone();
let prefix = X2(Parent, Parachain(1000));
let wrong_parachain = X2(Parent, Parachain(1001));
let wrong_account = X3(Parent, Parachain(1000), AccountIndex64 { network: Any, index: 24 });
let no_parents = X1(Parachain(1000));
let too_many_parents = X3(Parent, Parent, Parachain(1000));

assert!(full.starts_with(&identity));
assert!(full.starts_with(&prefix));
assert!(!full.starts_with(&wrong_parachain));
assert!(!full.starts_with(&wrong_account));
assert!(!full.starts_with(&no_parents));
assert!(!full.starts_with(&too_many_parents));
}

#[test]
fn append_with_works() {
let acc = AccountIndex64 { network: Any, index: 23 };
Expand Down
64 changes: 58 additions & 6 deletions xcm/src/v1/multilocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,24 @@ impl MultiLocation {
self.interior.match_and_split(&prefix.interior)
}

/// Returns whether `self` has the same number of parents as `prefix` and its junctions begins
/// with the junctions of `prefix`.
///
/// # Example
/// ```rust
/// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation};
/// let m = MultiLocation::new(1, X3(PalletInstance(3), OnlyChild, OnlyChild));
/// assert!(m.starts_with(&MultiLocation::new(1, X1(PalletInstance(3)))));
/// assert!(!m.starts_with(&MultiLocation::new(1, X1(GeneralIndex(99)))));
/// assert!(!m.starts_with(&MultiLocation::new(0, X1(PalletInstance(3)))));
/// ```
pub fn starts_with(&self, prefix: &MultiLocation) -> bool {
if self.parents != prefix.parents {
return false
}
self.interior.starts_with(&prefix.interior)
}

/// Mutate `self` so that it is suffixed with `suffix`.
///
/// Does not modify `self` and returns `Err` with `suffix` in case of overflow.
Expand Down Expand Up @@ -801,15 +819,29 @@ impl Junctions {
/// # }
/// ```
pub fn match_and_split(&self, prefix: &Junctions) -> Option<&Junction> {
if prefix.len() + 1 != self.len() {
if prefix.len() + 1 != self.len() || !self.starts_with(prefix) {
return None
}
for i in 0..prefix.len() {
if prefix.at(i) != self.at(i) {
return None
}
self.at(prefix.len())
}

/// Returns whether `self` begins with or is equal to `prefix`.
///
/// # Example
/// ```rust
/// # use xcm::v1::{Junctions::*, Junction::*};
/// let mut j = X3(Parachain(2), PalletInstance(3), OnlyChild);
/// assert!(j.starts_with(&X2(Parachain(2), PalletInstance(3))));
/// assert!(j.starts_with(&j));
/// assert!(j.starts_with(&X1(Parachain(2))));
/// assert!(!j.starts_with(&X1(Parachain(999))));
/// assert!(!j.starts_with(&X4(Parachain(2), PalletInstance(3), OnlyChild, OnlyChild)));
/// ```
pub fn starts_with(&self, prefix: &Junctions) -> bool {
if self.len() < prefix.len() {
return false
}
return self.at(prefix.len())
prefix.iter().zip(self.iter()).all(|(l, r)| l == r)
}
}

Expand Down Expand Up @@ -929,6 +961,26 @@ mod tests {
assert_eq!(m.match_and_split(&m), None);
}

#[test]
fn starts_with_works() {
let full: MultiLocation =
(Parent, Parachain(1000), AccountId32 { network: Any, id: [0; 32] }).into();
let identity: MultiLocation = full.clone();
let prefix: MultiLocation = (Parent, Parachain(1000)).into();
let wrong_parachain: MultiLocation = (Parent, Parachain(1001)).into();
let wrong_account: MultiLocation =
(Parent, Parachain(1000), AccountId32 { network: Any, id: [1; 32] }).into();
let no_parents: MultiLocation = (Parachain(1000)).into();
let too_many_parents: MultiLocation = (Parent, Parent, Parachain(1000)).into();

assert!(full.starts_with(&identity));
assert!(full.starts_with(&prefix));
assert!(!full.starts_with(&wrong_parachain));
assert!(!full.starts_with(&wrong_account));
assert!(!full.starts_with(&no_parents));
assert!(!full.starts_with(&too_many_parents));
}

#[test]
fn append_with_works() {
let acc = AccountIndex64 { network: Any, index: 23 };
Expand Down

0 comments on commit 8b9f580

Please sign in to comment.