-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Reanchor should return canonical location #4470
Changes from 3 commits
a3b968e
db7cd5f
55da4ea
b441587
95e6bab
f4a3439
81e2396
2a46816
cf270f2
6306c84
915b5d7
b4b1cbb
0dc5a28
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -323,6 +323,60 @@ impl MultiLocation { | |
| } | ||
| Ok(()) | ||
| } | ||
|
|
||
| /// Mutate `self` so that it represents the same location from the point of view of `target`. | ||
| /// The context of `self` is provided as `ancestry`. | ||
| /// | ||
| /// Does not modify `self` in case of overflow. | ||
| pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> { | ||
| // TODO: Optimize this. | ||
|
|
||
| // 1. Use our `ancestry` to figure out how the `target` would address us. | ||
| let inverted_target = ancestry.inverted(target)?; | ||
|
|
||
| // 2. Prepend `inverted_target` to `self` to get self's location from the perspective of | ||
| // `target`. | ||
| self.prepend_with(inverted_target).map_err(|_| ())?; | ||
|
|
||
| // 3. Given that we know some of `target` ancestry, ensure that any parents in `self` are | ||
| // strictly needed. | ||
| self.simplify(target.interior()); | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| /// Treating `self` as a context, determine how it would be referenced by a `target` location. | ||
| pub fn inverted(&self, target: &MultiLocation) -> Result<MultiLocation, ()> { | ||
gavofyork marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| use Junction::OnlyChild; | ||
| let mut ancestry = self.clone(); | ||
| let mut junctions = Junctions::Here; | ||
| for _ in 0..target.parent_count() { | ||
| junctions = junctions | ||
| .pushed_with(ancestry.interior.take_last().unwrap_or(OnlyChild)) | ||
gavofyork marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| .map_err(|_| ())?; | ||
| } | ||
| let parents = target.interior().len() as u8; | ||
| Ok(MultiLocation::new(parents, junctions)) | ||
| } | ||
|
|
||
| /// Remove any unneeded parents/junctions in `self` based on the given context it will be | ||
| /// interpreted in. | ||
| pub fn simplify(&mut self, context: &Junctions) { | ||
| if context.len() < self.parents as usize { | ||
| // Not enough context | ||
| return | ||
| } | ||
| while self.parents > 0 { | ||
| let maybe = context.at(context.len() - (self.parents as usize)); | ||
| match (self.interior.first(), maybe) { | ||
| (Some(i), Some(j)) if i == j => { | ||
| self.interior.take_first(); | ||
| self.parents -= 1; | ||
| }, | ||
| _ => break, | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// A unit struct which can be converted into a `MultiLocation` of `parents` value 1. | ||
|
|
@@ -773,9 +827,76 @@ impl TryFrom<MultiLocation> for Junctions { | |
| #[cfg(test)] | ||
| mod tests { | ||
| use super::{Ancestor, AncestorThen, Junctions::*, MultiLocation, Parent, ParentThen}; | ||
| use crate::opaque::v1::{Junction::*, NetworkId::Any}; | ||
| use crate::opaque::v1::{Junction::*, NetworkId::*}; | ||
| use parity_scale_codec::{Decode, Encode}; | ||
|
|
||
| #[test] | ||
| fn inverted_works() { | ||
| let ancestry: MultiLocation = (Parachain(1000), PalletInstance(42)).into(); | ||
| let target = (Parent, PalletInstance(69)).into(); | ||
| let expected = (Parent, PalletInstance(42)).into(); | ||
| let inverted = ancestry.inverted(&target).unwrap(); | ||
| assert_eq!(inverted, expected); | ||
gavofyork marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| #[test] | ||
| fn simplify_basic_works() { | ||
| let mut location: MultiLocation = | ||
| (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); | ||
| let context = X2(Parachain(1000), PalletInstance(42)); | ||
| let expected = GeneralIndex(69).into(); | ||
| location.simplify(&context); | ||
| assert_eq!(location, expected); | ||
|
|
||
| let mut location: MultiLocation = (Parent, PalletInstance(42), GeneralIndex(69)).into(); | ||
| let context = X1(PalletInstance(42)); | ||
| let expected = GeneralIndex(69).into(); | ||
| location.simplify(&context); | ||
| assert_eq!(location, expected); | ||
|
|
||
| let mut location: MultiLocation = (Parent, PalletInstance(42), GeneralIndex(69)).into(); | ||
| let context = X2(Parachain(1000), PalletInstance(42)); | ||
| let expected = GeneralIndex(69).into(); | ||
| location.simplify(&context); | ||
| assert_eq!(location, expected); | ||
|
|
||
| let mut location: MultiLocation = | ||
| (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); | ||
| let context = X3(OnlyChild, Parachain(1000), PalletInstance(42)); | ||
| let expected = GeneralIndex(69).into(); | ||
| location.simplify(&context); | ||
| assert_eq!(location, expected); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this pass? Because it does. I am having a really hard time understanding this function's behavior.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. All Local In order to normalise it, we need to detect and remove this case. In this case the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I understand now. Basically, we use context and the front of the multilocation and cancel out all "redundant" data, and normalize it.
This was very key. Thanks.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where I can read about stuff happening here? except code:) I read all papers on xcm I found and ported acala tests. so found reanchor stuff. do not get what it is. actually security of xcm is very unclear.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dzmitry-lahoda Substrate/Polkadot doesn't have a concept of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. :) Substrate/Polkadot has the location and general key concept. why would somebody trust these are genuine? Asset with location FooBar. Parachain X xcmed FooBar from X to Z. Why would we trust that FooBar was not just minted?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you are not understanding that every consensus system (or blockchain in layman terms) is responsible for interpreting incoming XCM messages correctly. As such, how a chain decides to interpret Thus, would you please raise this issue to Acala/Karura? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still not clear. Kusama can have KSM. Kusama can send (if we call its XCM pallet) to send KSM to paracahain. Can someone send KSM back to Kusama? Does Kusama tracks all places it send KSM to be equal? I mean DOT. Polkadot is relay chain. Does DOT sent via XCM has any security/alignment after it was send from Polkadot to Parachain? So right now I speak about Relay and some abstract Parachain. And some 2 abstract parachains having DOT/KSM. How these could interpret correctly message with asset (1, Here) origin?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
As I mentioned in my first message, reanchoring happens automatically behind the scenes and doesn't require any user input. If you are wondering about how assets are transferred between parachains, I have an answer on Stack Exchange that describes in detail how it is done, and how the trust issues are resolved. |
||
| } | ||
|
|
||
| #[test] | ||
| fn simplify_incompatible_location_fails() { | ||
| let mut location: MultiLocation = | ||
| (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); | ||
| let context = X3(Parachain(1000), PalletInstance(42), GeneralIndex(42)); | ||
| let expected = | ||
| (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); | ||
| location.simplify(&context); | ||
| assert_eq!(location, expected); | ||
|
|
||
| let mut location: MultiLocation = | ||
| (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); | ||
| let context = X1(Parachain(1000)); | ||
| let expected = | ||
| (Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into(); | ||
| location.simplify(&context); | ||
| assert_eq!(location, expected); | ||
| } | ||
|
|
||
| #[test] | ||
| fn reanchor_works() { | ||
| let mut id: MultiLocation = (Parent, Parachain(1000), GeneralIndex(42)).into(); | ||
| let ancestry = Parachain(2000).into(); | ||
| let target = (Parent, Parachain(1000)).into(); | ||
| let expected = GeneralIndex(42).into(); | ||
| id.reanchor(&target, &ancestry).unwrap(); | ||
| assert_eq!(id, expected); | ||
| } | ||
|
|
||
| #[test] | ||
| fn encode_and_decode_works() { | ||
| let m = MultiLocation { | ||
|
|
||

Uh oh!
There was an error while loading. Please reload this page.