XCM docs and tests (#2948)

* WIP

* add tests and docs for DoubleEncoded

* reformat parent_count

* add test for match_and_split

* fix append_with docs and add tests

* move Parachain enum variant to tuple

* Fix stuff

* add to append test

* simplify match

* formatting

* format and extend doc comments (including examples)

* fix typo

* add some doc comments

* add test for location inverter

* Add more tests/docs

* Fix build

* matches fungibles

* currency adapter.

* add more tests for location inverter

* extract max length magic number into constant

* adapters.

* Apply suggestions from code review

* Final touches.

* Repot and fixes

* Remove last todo

* Apply suggestions from code review

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-builder/src/barriers.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-builder/src/barriers.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-builder/src/currency_adapter.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-builder/src/filter_asset_location.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-builder/src/matches_fungible.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-executor/src/traits/conversion.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-executor/src/traits/conversion.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-executor/src/traits/transact_asset.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

* Update xcm/xcm-executor/src/traits/should_execute.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

Co-authored-by: kianenigma <kian@parity.io>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Alexander Popiak
2021-06-03 04:01:25 +02:00
committed by GitHub
parent 512b366280
commit a28d4a5c5d
22 changed files with 715 additions and 200 deletions
+2
View File
@@ -137,7 +137,9 @@ impl Id {
} }
} }
/// Determine if a parachain is a system parachain or not.
pub trait IsSystem { pub trait IsSystem {
/// Returns `true` if a parachain is a system parachain, `false` otherwise.
fn is_system(&self) -> bool; fn is_system(&self) -> bool;
} }
+42 -2
View File
@@ -17,6 +17,8 @@
use alloc::vec::Vec; use alloc::vec::Vec;
use parity_scale_codec::{Encode, Decode}; use parity_scale_codec::{Encode, Decode};
/// Wrapper around the encoded and decoded versions of a value.
/// Caches the decoded value once computed.
#[derive(Encode, Decode)] #[derive(Encode, Decode)]
#[codec(encode_bound())] #[codec(encode_bound())]
#[codec(decode_bound())] #[codec(decode_bound())]
@@ -29,11 +31,12 @@ pub struct DoubleEncoded<T> {
impl<T> Clone for DoubleEncoded<T> { impl<T> Clone for DoubleEncoded<T> {
fn clone(&self) -> Self { Self { encoded: self.encoded.clone(), decoded: None } } fn clone(&self) -> Self { Self { encoded: self.encoded.clone(), decoded: None } }
} }
impl<T> Eq for DoubleEncoded<T> {
}
impl<T> PartialEq for DoubleEncoded<T> { impl<T> PartialEq for DoubleEncoded<T> {
fn eq(&self, other: &Self) -> bool { self.encoded.eq(&other.encoded) } fn eq(&self, other: &Self) -> bool { self.encoded.eq(&other.encoded) }
} }
impl<T> Eq for DoubleEncoded<T> {}
impl<T> core::fmt::Debug for DoubleEncoded<T> { impl<T> core::fmt::Debug for DoubleEncoded<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { self.encoded.fmt(f) } fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { self.encoded.fmt(f) }
} }
@@ -46,29 +49,66 @@ impl<T> From<Vec<u8>> for DoubleEncoded<T> {
impl<T> DoubleEncoded<T> { impl<T> DoubleEncoded<T> {
pub fn into<S>(self) -> DoubleEncoded<S> { DoubleEncoded::from(self) } pub fn into<S>(self) -> DoubleEncoded<S> { DoubleEncoded::from(self) }
pub fn from<S>(e: DoubleEncoded<S>) -> Self { pub fn from<S>(e: DoubleEncoded<S>) -> Self {
Self { Self {
encoded: e.encoded, encoded: e.encoded,
decoded: None, decoded: None,
} }
} }
/// Provides an API similar to `AsRef` that provides access to the inner value.
/// `AsRef` implementation would expect an `&Option<T>` return type.
pub fn as_ref(&self) -> Option<&T> { pub fn as_ref(&self) -> Option<&T> {
self.decoded.as_ref() self.decoded.as_ref()
} }
} }
impl<T: Decode> DoubleEncoded<T> { impl<T: Decode> DoubleEncoded<T> {
/// Decode the inner encoded value and store it.
/// Returns a reference to the value in case of success and `Err(())` in case the decoding fails.
pub fn ensure_decoded(&mut self) -> Result<&T, ()> { pub fn ensure_decoded(&mut self) -> Result<&T, ()> {
if self.decoded.is_none() { if self.decoded.is_none() {
self.decoded = T::decode(&mut &self.encoded[..]).ok(); self.decoded = T::decode(&mut &self.encoded[..]).ok();
} }
self.decoded.as_ref().ok_or(()) self.decoded.as_ref().ok_or(())
} }
/// Move the decoded value out or (if not present) decode `encoded`.
pub fn take_decoded(&mut self) -> Result<T, ()> { pub fn take_decoded(&mut self) -> Result<T, ()> {
self.decoded.take().or_else(|| T::decode(&mut &self.encoded[..]).ok()).ok_or(()) self.decoded.take().or_else(|| T::decode(&mut &self.encoded[..]).ok()).ok_or(())
} }
/// Provides an API similar to `TryInto` that allows fallible conversion to the inner value type.
/// `TryInto` implementation would collide with std blanket implementation based on `TryFrom`.
pub fn try_into(mut self) -> Result<T, ()> { pub fn try_into(mut self) -> Result<T, ()> {
self.ensure_decoded()?; self.ensure_decoded()?;
self.decoded.ok_or(()) self.decoded.ok_or(())
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ensure_decoded_works() {
let val: u64 = 42;
let mut encoded: DoubleEncoded<_> = Encode::encode(&val).into();
assert_eq!(encoded.ensure_decoded(), Ok(&val));
}
#[test]
fn take_decoded_works() {
let val: u64 = 42;
let mut encoded: DoubleEncoded<_> = Encode::encode(&val).into();
assert_eq!(encoded.take_decoded(), Ok(val));
}
#[test]
fn try_into_works() {
let val: u64 = 42;
let encoded: DoubleEncoded<_> = Encode::encode(&val).into();
assert_eq!(encoded.try_into(), Ok(val));
}
}
+93 -29
View File
@@ -61,35 +61,34 @@ pub enum AssetInstance {
/// ### Abstract identifiers /// ### Abstract identifiers
/// ///
/// Abstract identifiers are absolute identifiers that represent a notional asset which can exist within multiple /// Abstract identifiers are absolute identifiers that represent a notional asset which can exist within multiple
/// consensus systems. These tend to be simpler to deal with since their broad meaning is unchanged regardless stay /// consensus systems. These tend to be simpler to deal with since their broad meaning is unchanged regardless stay of
/// of the consensus system in which it is interpreted. /// the consensus system in which it is interpreted.
/// ///
/// However, in the attempt to provide uniformity across consensus systems, they may conflate different instantiations /// However, in the attempt to provide uniformity across consensus systems, they may conflate different instantiations
/// of some notional asset (e.g. the reserve asset and a local reserve-backed derivative of it) under the same name, /// of some notional asset (e.g. the reserve asset and a local reserve-backed derivative of it) under the same name,
/// leading to confusion. It also implies that one notional asset is accounted for locally in only one way. This may /// leading to confusion. It also implies that one notional asset is accounted for locally in only one way. This may not
/// not be the case, e.g. where there are multiple bridge instances each providing a bridged "BTC" token yet none /// be the case, e.g. where there are multiple bridge instances each providing a bridged "BTC" token yet none being
/// being fungible between the others. /// fungible between the others.
/// ///
/// Since they are meant to be absolute and universal, a global registry is needed to ensure that name collisions /// Since they are meant to be absolute and universal, a global registry is needed to ensure that name collisions do not
/// do not occur. /// occur.
/// ///
/// An abstract identifier is represented as a simple variable-size byte string. As of writing, no global registry /// An abstract identifier is represented as a simple variable-size byte string. As of writing, no global registry
/// exists and no proposals have been put forth for asset labeling. /// exists and no proposals have been put forth for asset labeling.
/// ///
/// ### Concrete identifiers /// ### Concrete identifiers
/// ///
/// Concrete identifiers are *relative identifiers* that specifically identify a single asset through its location in /// Concrete identifiers are *relative identifiers* that specifically identify a single asset through its location in a
/// a consensus system relative to the context interpreting. Use of a `MultiLocation` ensures that similar but non /// consensus system relative to the context interpreting. Use of a `MultiLocation` ensures that similar but non
/// fungible variants of the same underlying asset can be properly distinguished, and obviates the need for any kind /// fungible variants of the same underlying asset can be properly distinguished, and obviates the need for any kind of
/// of central registry. /// central registry.
/// ///
/// The limitation is that the asset identifier cannot be trivially copied between consensus /// The limitation is that the asset identifier cannot be trivially copied between consensus systems and must instead be
/// systems and must instead be "re-anchored" whenever being moved to a new consensus system, using the two systems' /// "re-anchored" whenever being moved to a new consensus system, using the two systems' relative paths.
/// relative paths.
/// ///
/// Throughout XCM, messages are authored such that *when interpreted from the receiver's point of view* they will /// Throughout XCM, messages are authored such that *when interpreted from the receiver's point of view* they will have
/// have the desired meaning/effect. This means that relative paths should always by constructed to be read from the /// the desired meaning/effect. This means that relative paths should always by constructed to be read from the point of
/// point of view of the receiving system, *which may be have a completely different meaning in the authoring system*. /// view of the receiving system, *which may be have a completely different meaning in the authoring system*.
/// ///
/// Concrete identifiers are the preferred way of identifying an asset since they are entirely unambiguous. /// Concrete identifiers are the preferred way of identifying an asset since they are entirely unambiguous.
/// ///
@@ -99,8 +98,8 @@ pub enum AssetInstance {
/// ///
/// - `<chain>/PalletInstance(<id>)` for a Frame chain with a single-asset pallet instance (such as an instance of the /// - `<chain>/PalletInstance(<id>)` for a Frame chain with a single-asset pallet instance (such as an instance of the
/// Balances pallet). /// Balances pallet).
/// - `<chain>/PalletInstance(<id>)/GeneralIndex(<index>)` for a Frame chain with an indexed multi-asset pallet /// - `<chain>/PalletInstance(<id>)/GeneralIndex(<index>)` for a Frame chain with an indexed multi-asset pallet instance
/// instance (such as an instance of the Assets pallet). /// (such as an instance of the Assets pallet).
/// - `<chain>/AccountId32` for an ERC-20-style single-asset smart-contract on a Frame-based contracts chain. /// - `<chain>/AccountId32` for an ERC-20-style single-asset smart-contract on a Frame-based contracts chain.
/// - `<chain>/AccountKey20` for an ERC-20-style single-asset smart-contract on an Ethereum-like chain. /// - `<chain>/AccountKey20` for an ERC-20-style single-asset smart-contract on an Ethereum-like chain.
/// ///
@@ -147,6 +146,9 @@ pub enum MultiAsset {
} }
impl MultiAsset { impl MultiAsset {
/// Returns `true` if the `MultiAsset` is a wildcard and can refer to classes of assets, instead of just one.
///
/// Typically can also be inferred by the name starting with `All`.
pub fn is_wildcard(&self) -> bool { pub fn is_wildcard(&self) -> bool {
match self { match self {
MultiAsset::None MultiAsset::None
@@ -209,7 +211,6 @@ impl MultiAsset {
fn is_concrete_fungible(&self, id: &MultiLocation) -> bool { fn is_concrete_fungible(&self, id: &MultiLocation) -> bool {
match self { match self {
MultiAsset::AllFungible => true, MultiAsset::AllFungible => true,
MultiAsset::AllConcreteFungible { id: i } MultiAsset::AllConcreteFungible { id: i }
| MultiAsset::ConcreteFungible { id: i, .. } | MultiAsset::ConcreteFungible { id: i, .. }
=> i == id, => i == id,
@@ -250,8 +251,13 @@ impl MultiAsset {
fn is_all(&self) -> bool { matches!(self, MultiAsset::All) } fn is_all(&self) -> bool { matches!(self, MultiAsset::All) }
/// Returns true if `self` is a super-set of the given `inner`.
///
/// Typically, any wildcard is never contained in anything else, and a wildcard can contain any other non-wildcard.
/// For more details, see the implementation and tests.
pub fn contains(&self, inner: &MultiAsset) -> bool { pub fn contains(&self, inner: &MultiAsset) -> bool {
use MultiAsset::*; use MultiAsset::*;
// Inner cannot be wild // Inner cannot be wild
if inner.is_wildcard() { return false } if inner.is_wildcard() { return false }
// Everything contains nothing. // Everything contains nothing.
@@ -273,15 +279,16 @@ impl MultiAsset {
AllConcreteNonFungible { class } => inner.is_concrete_non_fungible(class), AllConcreteNonFungible { class } => inner.is_concrete_non_fungible(class),
AllAbstractNonFungible { class } => inner.is_abstract_non_fungible(class), AllAbstractNonFungible { class } => inner.is_abstract_non_fungible(class),
ConcreteFungible { id, amount } ConcreteFungible { id, amount } => matches!(
=> matches!(inner, ConcreteFungible { id: i , amount: a } if i == id && a >= amount), inner,
AbstractFungible { id, amount } ConcreteFungible { id: inner_id , amount: inner_amount } if inner_id == id && amount >= inner_amount
=> matches!(inner, AbstractFungible { id: i , amount: a } if i == id && a >= amount), ),
ConcreteNonFungible { class, instance } AbstractFungible { id, amount } => matches!(
=> matches!(inner, ConcreteNonFungible { class: i , instance: a } if i == class && a == instance), inner,
AbstractNonFungible { class, instance } AbstractFungible { id: inner_id , amount: inner_amount } if inner_id == id && amount >= inner_amount
=> matches!(inner, AbstractNonFungible { class: i , instance: a } if i == class && a == instance), ),
ConcreteNonFungible { .. } => self == inner,
AbstractNonFungible { .. } => self == inner,
_ => false, _ => false,
} }
} }
@@ -313,3 +320,60 @@ impl TryFrom<VersionedMultiAsset> for MultiAsset {
} }
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn contains_works() {
use alloc::vec;
use MultiAsset::*;
// trivial case: all contains any non-wildcard.
assert!(All.contains(&None));
assert!(All.contains(&AbstractFungible { id: alloc::vec![99u8], amount: 1 }));
// trivial case: none contains nothing, except itself.
assert!(None.contains(&None));
assert!(!None.contains(&AllFungible));
assert!(!None.contains(&All));
// A bit more sneaky: Nothing can contain wildcard, even All ir the thing itself.
assert!(!All.contains(&All));
assert!(!All.contains(&AllFungible));
assert!(!AllFungible.contains(&AllFungible));
assert!(!AllNonFungible.contains(&AllNonFungible));
// For fungibles, containing is basically equality, or equal id with higher amount.
assert!(
!AbstractFungible { id: vec![99u8], amount: 99 }
.contains(&AbstractFungible { id: vec![1u8], amount: 99 })
);
assert!(
AbstractFungible { id: vec![99u8], amount: 99 }
.contains(&AbstractFungible { id: vec![99u8], amount: 99 })
);
assert!(
AbstractFungible { id: vec![99u8], amount: 99 }
.contains(&AbstractFungible { id: vec![99u8], amount: 9 })
);
assert!(
!AbstractFungible { id: vec![99u8], amount: 99 }
.contains(&AbstractFungible { id: vec![99u8], amount: 100 })
);
// For non-fungibles, containing is equality.
assert!(
!AbstractNonFungible {class: vec![99u8], instance: AssetInstance::Index { id: 9 } }
.contains(&AbstractNonFungible { class: vec![98u8], instance: AssetInstance::Index { id: 9 } })
);
assert!(
!AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index { id: 8 } }
.contains(&AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index { id: 9 } })
);
assert!(
AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index { id: 9 } }
.contains(&AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index { id: 9 } })
);
}
}
+137 -74
View File
@@ -66,6 +66,9 @@ pub enum MultiLocation {
X8(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction), X8(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction),
} }
/// Maximum number of junctions a multilocation can contain.
pub const MAX_MULTILOCATION_LENGTH: usize = 8;
impl From<Junction> for MultiLocation { impl From<Junction> for MultiLocation {
fn from(x: Junction) -> Self { fn from(x: Junction) -> Self {
MultiLocation::X1(x) MultiLocation::X1(x)
@@ -442,8 +445,18 @@ impl MultiLocation {
MultiLocationReverseIterator(self) MultiLocationReverseIterator(self)
} }
/// Ensures that self begins with `prefix` and that it has a single `Junction` item following. If /// Ensures that self begins with `prefix` and that it has a single `Junction` item following.
/// so, returns a reference to this `Junction` item. /// If so, returns a reference to this `Junction` item.
///
/// # Example
/// ```rust
/// # use xcm::v0::{MultiLocation::*, Junction::*};
/// # fn main() {
/// let mut m = X3(Parent, PalletInstance(3), OnlyChild);
/// assert_eq!(m.match_and_split(&X2(Parent, PalletInstance(3))), Some(&OnlyChild));
/// assert_eq!(m.match_and_split(&X1(Parent)), None);
/// # }
/// ```
pub fn match_and_split(&self, prefix: &MultiLocation) -> Option<&Junction> { pub fn match_and_split(&self, prefix: &MultiLocation) -> Option<&Junction> {
if prefix.len() + 1 != self.len() { if prefix.len() + 1 != self.len() {
return None return None
@@ -479,84 +492,69 @@ impl MultiLocation {
/// Returns the number of `Parent` junctions at the beginning of `self`. /// Returns the number of `Parent` junctions at the beginning of `self`.
pub fn parent_count(&self) -> usize { pub fn parent_count(&self) -> usize {
use Junction::Parent;
match self { match self {
MultiLocation::X8( MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent) => 8,
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent,
Junction::Parent, Junction::Parent, Junction::Parent
) => 8,
MultiLocation::X8( MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, ..) => 7,
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent) => 7,
Junction::Parent, Junction::Parent, ..
) => 7,
MultiLocation::X7(
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent,
Junction::Parent, Junction::Parent
) => 7,
MultiLocation::X8( MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, ..) => 6,
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, Parent, ..) => 6,
Junction::Parent, .. MultiLocation::X6(Parent, Parent, Parent, Parent, Parent, Parent) => 6,
) => 6,
MultiLocation::X7(
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent,
Junction::Parent, ..
) => 6,
MultiLocation::X6(
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent,
Junction::Parent
) => 6,
MultiLocation::X8( MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, ..) => 5,
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, .. MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, ..) => 5,
) => 5, MultiLocation::X6(Parent, Parent, Parent, Parent, Parent, ..) => 5,
MultiLocation::X7( MultiLocation::X5(Parent, Parent, Parent, Parent, Parent) => 5,
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..
) => 5,
MultiLocation::X6(
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..
) => 5,
MultiLocation::X5(
Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent
) => 5,
MultiLocation::X8(Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..) => 4, MultiLocation::X8(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X7(Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..) => 4, MultiLocation::X7(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X6(Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..) => 4, MultiLocation::X6(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X5(Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent, ..) => 4, MultiLocation::X5(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X4(Junction::Parent, Junction::Parent, Junction::Parent, Junction::Parent) => 4, MultiLocation::X4(Parent, Parent, Parent, Parent) => 4,
MultiLocation::X8(Junction::Parent, Junction::Parent, Junction::Parent, ..) => 3, MultiLocation::X8(Parent, Parent, Parent, ..) => 3,
MultiLocation::X7(Junction::Parent, Junction::Parent, Junction::Parent, ..) => 3, MultiLocation::X7(Parent, Parent, Parent, ..) => 3,
MultiLocation::X6(Junction::Parent, Junction::Parent, Junction::Parent, ..) => 3, MultiLocation::X6(Parent, Parent, Parent, ..) => 3,
MultiLocation::X5(Junction::Parent, Junction::Parent, Junction::Parent, ..) => 3, MultiLocation::X5(Parent, Parent, Parent, ..) => 3,
MultiLocation::X4(Junction::Parent, Junction::Parent, Junction::Parent, ..) => 3, MultiLocation::X4(Parent, Parent, Parent, ..) => 3,
MultiLocation::X3(Junction::Parent, Junction::Parent, Junction::Parent) => 3, MultiLocation::X3(Parent, Parent, Parent) => 3,
MultiLocation::X8(Junction::Parent, Junction::Parent, ..) => 2, MultiLocation::X8(Parent, Parent, ..) => 2,
MultiLocation::X7(Junction::Parent, Junction::Parent, ..) => 2, MultiLocation::X7(Parent, Parent, ..) => 2,
MultiLocation::X6(Junction::Parent, Junction::Parent, ..) => 2, MultiLocation::X6(Parent, Parent, ..) => 2,
MultiLocation::X5(Junction::Parent, Junction::Parent, ..) => 2, MultiLocation::X5(Parent, Parent, ..) => 2,
MultiLocation::X4(Junction::Parent, Junction::Parent, ..) => 2, MultiLocation::X4(Parent, Parent, ..) => 2,
MultiLocation::X3(Junction::Parent, Junction::Parent, ..) => 2, MultiLocation::X3(Parent, Parent, ..) => 2,
MultiLocation::X2(Junction::Parent, Junction::Parent) => 2, MultiLocation::X2(Parent, Parent) => 2,
MultiLocation::X8(Junction::Parent, ..) => 1, MultiLocation::X8(Parent, ..) => 1,
MultiLocation::X7(Junction::Parent, ..) => 1, MultiLocation::X7(Parent, ..) => 1,
MultiLocation::X6(Junction::Parent, ..) => 1, MultiLocation::X6(Parent, ..) => 1,
MultiLocation::X5(Junction::Parent, ..) => 1, MultiLocation::X5(Parent, ..) => 1,
MultiLocation::X4(Junction::Parent, ..) => 1, MultiLocation::X4(Parent, ..) => 1,
MultiLocation::X3(Junction::Parent, ..) => 1, MultiLocation::X3(Parent, ..) => 1,
MultiLocation::X2(Junction::Parent, ..) => 1, MultiLocation::X2(Parent, ..) => 1,
MultiLocation::X1(Junction::Parent) => 1, MultiLocation::X1(Parent) => 1,
_ => 0, _ => 0,
} }
} }
/// Mutate `self` so that it is suffixed with `prefix`. The correct normalized form is returned, removing any /// Mutate `self` so that it is suffixed with `suffix`. The correct normalized form is returned,
/// internal `Parent`s. /// removing any internal [Non-Parent, `Parent`] combinations.
/// ///
/// Does not modify `self` and returns `Err` with `prefix` in case of overflow. /// Does not modify `self` and returns `Err` with `suffix` in case of overflow.
///
/// # Example
/// ```rust
/// # use xcm::v0::{MultiLocation::*, Junction::*};
/// # fn main() {
/// let mut m = X3(Parent, Parachain(21), OnlyChild);
/// assert_eq!(m.append_with(X2(Parent, PalletInstance(3))), Ok(()));
/// assert_eq!(m, X3(Parent, Parachain(21), PalletInstance(3)));
/// # }
/// ```
pub fn append_with(&mut self, suffix: MultiLocation) -> Result<(), MultiLocation> { pub fn append_with(&mut self, suffix: MultiLocation) -> Result<(), MultiLocation> {
let mut prefix = suffix; let mut prefix = suffix;
core::mem::swap(self, &mut prefix); core::mem::swap(self, &mut prefix);
@@ -570,21 +568,31 @@ impl MultiLocation {
} }
} }
/// Mutate `self` so that it is prefixed with `prefix`. The correct normalized form is returned, removing any /// Mutate `self` so that it is prefixed with `prefix`. The correct normalized form is returned,
/// internal `Parent`s. /// removing any internal [Non-Parent, `Parent`] combinations.
/// ///
/// Does not modify `self` and returns `Err` with `prefix` in case of overflow. /// Does not modify `self` and returns `Err` with `prefix` in case of overflow.
///
/// # Example
/// ```rust
/// # use xcm::v0::{MultiLocation::*, Junction::*, NetworkId::Any};
/// # fn main() {
/// let mut m = X3(Parent, Parent, PalletInstance(3));
/// assert_eq!(m.prepend_with(X3(Parent, Parachain(21), OnlyChild)), Ok(()));
/// assert_eq!(m, X2(Parent, PalletInstance(3)));
/// # }
/// ```
pub fn prepend_with(&mut self, prefix: MultiLocation) -> Result<(), MultiLocation> { pub fn prepend_with(&mut self, prefix: MultiLocation) -> Result<(), MultiLocation> {
let self_parents = self.parent_count(); let self_parents = self.parent_count();
let prefix_rest = prefix.len() - prefix.parent_count(); let prefix_rest = prefix.len() - prefix.parent_count();
let skipped = self_parents.min(prefix_rest); let skipped = self_parents.min(prefix_rest);
if self.len() + prefix.len() - 2 * skipped > 8 { if self.len() + prefix.len() - 2 * skipped > MAX_MULTILOCATION_LENGTH {
return Err(prefix); return Err(prefix);
} }
let mut prefix = prefix; let mut prefix = prefix;
while match (prefix.last(), self.first()) { while match (prefix.last(), self.first()) {
(Some(x), Some(Junction::Parent)) if x != &Junction::Parent => { (Some(x), Some(Junction::Parent)) if x.is_interior() => {
prefix.take_last(); prefix.take_last();
self.take_first(); self.take_first();
true true
@@ -593,13 +601,25 @@ impl MultiLocation {
} {} } {}
for j in prefix.into_iter_rev() { for j in prefix.into_iter_rev() {
self.push_front(j).expect("len + prefix minus 2*skipped is less than 4; qed"); self.push_front(j).expect("len + prefix minus 2*skipped is less than max length; qed");
} }
Ok(()) Ok(())
} }
/// Returns true iff `self` is an interior location. For this it may not contain any `Junction`s for which /// Returns true iff `self` is an interior location. For this it may not contain any `Junction`s
/// `Junction::is_interior` returns `false`. This /// for which `Junction::is_interior` returns `false`. This is generally true, except for the
/// `Parent` item.
///
/// # Example
/// ```rust
/// # use xcm::v0::{MultiLocation::*, Junction::*, NetworkId::Any};
/// # fn main() {
/// let parent = X1(Parent);
/// assert_eq!(parent.is_interior(), false);
/// let m = X2(PalletInstance(12), AccountIndex64 { network: Any, index: 23 });
/// assert_eq!(m.is_interior(), true);
/// # }
/// ```
pub fn is_interior(&self) -> bool { pub fn is_interior(&self) -> bool {
self.iter().all(Junction::is_interior) self.iter().all(Junction::is_interior)
} }
@@ -619,3 +639,46 @@ impl TryFrom<VersionedMultiLocation> for MultiLocation {
} }
} }
} }
#[cfg(test)]
mod tests {
use super::MultiLocation::*;
use crate::opaque::v0::{Junction::*, NetworkId::Any};
#[test]
fn match_and_split_works() {
let m = X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 });
assert_eq!(m.match_and_split(&X1(Parent)), None);
assert_eq!(
m.match_and_split(&X2(Parent, Parachain(42))),
Some(&AccountIndex64 { network: Any, index: 23 })
);
assert_eq!(m.match_and_split(&m), None);
}
#[test]
fn append_with_works() {
let acc = AccountIndex64 { network: Any, index: 23 };
let mut m = X2(Parent, Parachain(42));
assert_eq!(m.append_with(X2(PalletInstance(3), acc.clone())), Ok(()));
assert_eq!(m, X4(Parent, Parachain(42), PalletInstance(3), acc.clone()));
// cannot append to create overly long multilocation
let acc = AccountIndex64 { network: Any, index: 23 };
let mut m = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parachain(42));
let suffix = X2(PalletInstance(3), acc.clone());
assert_eq!(m.append_with(suffix.clone()), Err(suffix));
}
#[test]
fn prepend_with_works() {
let mut m = X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 });
assert_eq!(m.prepend_with(X2(Parent, OnlyChild)), Ok(()));
assert_eq!(m, X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 }));
// cannot prepend to create overly long multilocation
let mut m = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parachain(42));
let prefix = X2(Parent, Parent);
assert_eq!(m.prepend_with(prefix.clone()), Err(prefix));
}
}
+64 -4
View File
@@ -162,13 +162,72 @@ impl<C> ExecuteXcm<C> for () {
/// Utility for sending an XCM message. /// Utility for sending an XCM message.
/// ///
/// These can be amalgamated in tuples to form sophisticated routing systems. /// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return
/// `CannotReachDestination` to pass the execution to the next sender item. Note that each `CannotReachDestination`
/// might alter the destination and the xcm message for to the next router.
///
///
/// # Example
/// ```rust
/// # use xcm::v0::{MultiLocation, Xcm, Junction, Error, OriginKind, SendXcm, Result};
/// # use parity_scale_codec::Encode;
///
/// /// A sender that only passes the message through and does nothing.
/// struct Sender1;
/// impl SendXcm for Sender1 {
/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
/// return Err(Error::CannotReachDestination(destination, message))
/// }
/// }
///
/// /// A sender that accepts a message that has an X2 junction, otherwise stops the routing.
/// struct Sender2;
/// impl SendXcm for Sender2 {
/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
/// if let MultiLocation::X2(j1, j2) = destination {
/// Ok(())
/// } else {
/// Err(Error::Undefined)
/// }
/// }
/// }
///
/// /// A sender that accepts a message from an X1 parent junction, passing through otherwise.
/// struct Sender3;
/// impl SendXcm for Sender3 {
/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
/// match destination {
/// MultiLocation::X1(j) if j == Junction::Parent => Ok(()),
/// _ => Err(Error::CannotReachDestination(destination, message)),
/// }
/// }
/// }
///
/// // A call to send via XCM. We don't really care about this.
/// # fn main() {
/// let call: Vec<u8> = ().encode();
/// let message = Xcm::Transact { origin_type: OriginKind::Superuser, require_weight_at_most: 0, call: call.into() };
/// let destination = MultiLocation::X1(Junction::Parent);
///
/// assert!(
/// // Sender2 will block this.
/// <(Sender1, Sender2, Sender3) as SendXcm>::send_xcm(destination.clone(), message.clone())
/// .is_err()
/// );
///
/// assert!(
/// // Sender3 will catch this.
/// <(Sender1, Sender3) as SendXcm>::send_xcm(destination.clone(), message.clone())
/// .is_ok()
/// );
/// # }
/// ```
pub trait SendXcm { pub trait SendXcm {
/// Send an XCM `message` to a given `destination`. /// Send an XCM `message` to a given `destination`.
/// ///
/// If it is not a destination which can be reached with this type but possibly could by others, /// If it is not a destination which can be reached with this type but possibly could by others, then it *MUST*
/// then it *MUST* return `CannotReachDestination`. Any other error will cause the tuple implementation to /// return `CannotReachDestination`. Any other error will cause the tuple implementation to exit early without
/// exit early without trying other type fields. /// trying other type fields.
fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result; fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result;
} }
@@ -176,6 +235,7 @@ pub trait SendXcm {
impl SendXcm for Tuple { impl SendXcm for Tuple {
fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result { fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
for_tuples!( #( for_tuples!( #(
// we shadow `destination` and `message` in each expansion for the next one.
let (destination, message) = match Tuple::send_xcm(destination, message) { let (destination, message) = match Tuple::send_xcm(destination, message) {
Err(Error::CannotReachDestination(d, m)) => (d, m), Err(Error::CannotReachDestination(d, m)) => (d, m),
o @ _ => return o, o @ _ => return o,
+9
View File
@@ -14,12 +14,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. // along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Various implementations for `ShouldExecute`.
use sp_std::{result::Result, marker::PhantomData}; use sp_std::{result::Result, marker::PhantomData};
use xcm::v0::{Xcm, Order, MultiLocation, Junction}; use xcm::v0::{Xcm, Order, MultiLocation, Junction};
use frame_support::{ensure, traits::Contains, weights::Weight}; use frame_support::{ensure, traits::Contains, weights::Weight};
use xcm_executor::traits::{OnResponse, ShouldExecute}; use xcm_executor::traits::{OnResponse, ShouldExecute};
use polkadot_parachain::primitives::IsSystem; use polkadot_parachain::primitives::IsSystem;
/// Execution barrier that just takes `shallow_weight` from `weight_credit`.
pub struct TakeWeightCredit; pub struct TakeWeightCredit;
impl ShouldExecute for TakeWeightCredit { impl ShouldExecute for TakeWeightCredit {
fn should_execute<Call>( fn should_execute<Call>(
@@ -34,6 +37,8 @@ impl ShouldExecute for TakeWeightCredit {
} }
} }
/// Allows execution from `origin` if it is contained in `T` (i.e. `T::Contains(origin)`) taking payments into
/// account.
pub struct AllowTopLevelPaidExecutionFrom<T>(PhantomData<T>); pub struct AllowTopLevelPaidExecutionFrom<T>(PhantomData<T>);
impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFrom<T> { impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFrom<T> {
fn should_execute<Call>( fn should_execute<Call>(
@@ -59,6 +64,8 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFro
} }
} }
/// Allows execution from any origin that is contained in `T` (i.e. `T::Contains(origin)`) without any payments.
/// Use only for executions from trusted origin groups.
pub struct AllowUnpaidExecutionFrom<T>(PhantomData<T>); pub struct AllowUnpaidExecutionFrom<T>(PhantomData<T>);
impl<T: Contains<MultiLocation>> ShouldExecute for AllowUnpaidExecutionFrom<T> { impl<T: Contains<MultiLocation>> ShouldExecute for AllowUnpaidExecutionFrom<T> {
fn should_execute<Call>( fn should_execute<Call>(
@@ -73,6 +80,7 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowUnpaidExecutionFrom<T> {
} }
} }
/// Allows a message only if it is from a system-level child parachain.
pub struct IsChildSystemParachain<ParaId>(PhantomData<ParaId>); pub struct IsChildSystemParachain<ParaId>(PhantomData<ParaId>);
impl< impl<
ParaId: IsSystem + From<u32>, ParaId: IsSystem + From<u32>,
@@ -82,6 +90,7 @@ impl<
} }
} }
/// Allows only messages if the generic `ResponseHandler` expects them via `expecting_response`.
pub struct AllowKnownQueryResponses<ResponseHandler>(PhantomData<ResponseHandler>); pub struct AllowKnownQueryResponses<ResponseHandler>(PhantomData<ResponseHandler>);
impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<ResponseHandler> { impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<ResponseHandler> {
fn should_execute<Call>( fn should_execute<Call>(
@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. // along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Adapters to work with `frame_support::traits::Currency` through XCM.
use sp_std::{result, convert::TryInto, marker::PhantomData}; use sp_std::{result, convert::TryInto, marker::PhantomData};
use xcm::v0::{Error as XcmError, Result, MultiAsset, MultiLocation}; use xcm::v0::{Error as XcmError, Result, MultiAsset, MultiLocation};
use sp_runtime::traits::{SaturatedConversion, CheckedSub}; use sp_runtime::traits::{SaturatedConversion, CheckedSub};
@@ -33,16 +35,51 @@ enum Error {
impl From<Error> for XcmError { impl From<Error> for XcmError {
fn from(e: Error) -> Self { fn from(e: Error) -> Self {
use XcmError::FailedToTransactAsset;
match e { match e {
Error::AssetNotFound => XcmError::FailedToTransactAsset("AssetNotFound"), Error::AssetNotFound => FailedToTransactAsset("AssetNotFound"),
Error::AccountIdConversionFailed => Error::AccountIdConversionFailed => FailedToTransactAsset("AccountIdConversionFailed"),
XcmError::FailedToTransactAsset("AccountIdConversionFailed"), Error::AmountToBalanceConversionFailed => FailedToTransactAsset("AmountToBalanceConversionFailed"),
Error::AmountToBalanceConversionFailed =>
XcmError::FailedToTransactAsset("AmountToBalanceConversionFailed"),
} }
} }
} }
/// Simple adapter to use a currency as asset transactor. This type can be used as `type AssetTransactor` in
/// `xcm::Config`.
///
/// # Example
/// ```
/// use frame_support::parameter_types;
/// use xcm::v0::{MultiLocation, Junction};
/// use xcm_builder::{ParentIsDefault, CurrencyAdapter, IsConcrete};
///
/// /// Our chain's account id.
/// type AccountId = sp_runtime::AccountId32;
///
/// /// Our relay chain's location.
/// parameter_types! {
/// RelayChain: MultiLocation = MultiLocation::X1(Junction::Parent);
/// CheckingAccount: AccountId = Default::default();
/// }
///
/// /// Some items that implement `Convert<MultiLocation, AccountId>`. Can be more, but for now we just assume we accept
/// /// messages from the parent (relay chain).
/// pub type LocationConvertor = (ParentIsDefault<RelayChain>);
///
/// /// Final currency adapter. This can be used in `xcm::Config` to specify how asset related transactions happen.
/// pub type AssetTransactor = CurrencyAdapter<
/// // Use this balance type:
/// u128,
/// // The matcher: use the currency when the asset is a concrete asset in our relay chain.
/// IsConcrete<RelayChain>,
/// // The local convertor: default account of the parent relay chain.
/// LocationConvertor,
/// // Our chain's account ID type.
/// AccountId,
/// // The checking account. Can be any deterministic inaccessible account.
/// CheckingAccount,
/// >;
/// ```
pub struct CurrencyAdapter<Currency, Matcher, AccountIdConverter, AccountId, CheckedAccount>( pub struct CurrencyAdapter<Currency, Matcher, AccountIdConverter, AccountId, CheckedAccount>(
PhantomData<(Currency, Matcher, AccountIdConverter, AccountId, CheckedAccount)> PhantomData<(Currency, Matcher, AccountIdConverter, AccountId, CheckedAccount)>
); );
@@ -14,11 +14,14 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. // along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Various implementations of `FilterAssetLocation`.
use sp_std::marker::PhantomData; use sp_std::marker::PhantomData;
use xcm::v0::{MultiAsset, MultiLocation}; use xcm::v0::{MultiAsset, MultiLocation};
use frame_support::traits::Get; use frame_support::traits::Get;
use xcm_executor::traits::FilterAssetLocation; use xcm_executor::traits::FilterAssetLocation;
/// Accepts an asset IFF it is a native asset.
pub struct NativeAsset; pub struct NativeAsset;
impl FilterAssetLocation for NativeAsset { impl FilterAssetLocation for NativeAsset {
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
@@ -26,6 +29,7 @@ impl FilterAssetLocation for NativeAsset {
} }
} }
/// Accepts an asset if it is contained in the given `T`'s `Get` impl.
pub struct Case<T>(PhantomData<T>); pub struct Case<T>(PhantomData<T>);
impl<T: Get<(MultiAsset, MultiLocation)>> FilterAssetLocation for Case<T> { impl<T: Get<(MultiAsset, MultiLocation)>> FilterAssetLocation for Case<T> {
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
@@ -14,40 +14,16 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. // along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Adapters to work with `frame_support::traits::tokens::fungibles` through XCM.
use sp_std::{prelude::*, result, marker::PhantomData, borrow::Borrow}; use sp_std::{prelude::*, result, marker::PhantomData, borrow::Borrow};
use xcm::v0::{Error as XcmError, Result, MultiAsset, MultiLocation, Junction}; use xcm::v0::{Error as XcmError, Result, MultiAsset, MultiLocation, Junction};
use frame_support::traits::{Get, tokens::fungibles, Contains}; use frame_support::traits::{Get, tokens::fungibles, Contains};
use xcm_executor::traits::{TransactAsset, Convert}; use xcm_executor::traits::{TransactAsset, Convert, MatchesFungibles, Error as MatchError};
/// Asset transaction errors. /// Converter struct implementing `AssetIdConversion` converting a numeric asset ID (must be TryFrom/TryInto<u128>) into
pub enum Error { /// a `GeneralIndex` junction, prefixed by some `MultiLocation` value. The `MultiLocation` value will typically be a
/// Asset not found. /// `PalletInstance` junction.
AssetNotFound,
/// `MultiLocation` to `AccountId` conversion failed.
AccountIdConversionFailed,
/// `u128` amount to currency `Balance` conversion failed.
AmountToBalanceConversionFailed,
/// `MultiLocation` to `AssetId` conversion failed.
AssetIdConversionFailed,
}
impl From<Error> for XcmError {
fn from(e: Error) -> Self {
match e {
Error::AssetNotFound => XcmError::FailedToTransactAsset("AssetNotFound"),
Error::AccountIdConversionFailed =>
XcmError::FailedToTransactAsset("AccountIdConversionFailed"),
Error::AmountToBalanceConversionFailed =>
XcmError::FailedToTransactAsset("AmountToBalanceConversionFailed"),
Error::AssetIdConversionFailed =>
XcmError::FailedToTransactAsset("AssetIdConversionFailed"),
}
}
}
/// Converter struct implementing `AssetIdConversion` converting a numeric asset ID (must be TryFrom/TryInto<u128>)
/// into a `GeneralIndex` junction, prefixed by some `MultiLocation` value. The `MultiLocation` value will
/// typically be a `PalletInstance` junction.
pub struct AsPrefixedGeneralIndex<Prefix, AssetId, ConvertAssetId>(PhantomData<(Prefix, AssetId, ConvertAssetId)>); pub struct AsPrefixedGeneralIndex<Prefix, AssetId, ConvertAssetId>(PhantomData<(Prefix, AssetId, ConvertAssetId)>);
impl< impl<
Prefix: Get<MultiLocation>, Prefix: Get<MultiLocation>,
@@ -73,23 +49,6 @@ impl<
} }
} }
pub trait MatchesFungibles<AssetId, Balance> {
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), Error>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<
AssetId: Clone,
Balance: Clone,
> MatchesFungibles<AssetId, Balance> for Tuple {
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), Error> {
for_tuples!( #(
match Tuple::matches_fungibles(a) { o @ Ok(_) => return o, _ => () }
)* );
Err(Error::AssetNotFound)
}
}
pub struct ConvertedConcreteAssetId<AssetId, Balance, ConvertAssetId, ConvertBalance>( pub struct ConvertedConcreteAssetId<AssetId, Balance, ConvertAssetId, ConvertBalance>(
PhantomData<(AssetId, Balance, ConvertAssetId, ConvertBalance)> PhantomData<(AssetId, Balance, ConvertAssetId, ConvertBalance)>
); );
@@ -101,13 +60,13 @@ impl<
> MatchesFungibles<AssetId, Balance> for > MatchesFungibles<AssetId, Balance> for
ConvertedConcreteAssetId<AssetId, Balance, ConvertAssetId, ConvertBalance> ConvertedConcreteAssetId<AssetId, Balance, ConvertAssetId, ConvertBalance>
{ {
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), Error> { fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> {
let (id, amount) = match a { let (id, amount) = match a {
MultiAsset::ConcreteFungible { id, amount } => (id, amount), MultiAsset::ConcreteFungible { id, amount } => (id, amount),
_ => return Err(Error::AssetNotFound), _ => return Err(MatchError::AssetNotFound),
}; };
let what = ConvertAssetId::convert_ref(id).map_err(|_| Error::AssetIdConversionFailed)?; let what = ConvertAssetId::convert_ref(id).map_err(|_| MatchError::AssetIdConversionFailed)?;
let amount = ConvertBalance::convert_ref(amount).map_err(|_| Error::AmountToBalanceConversionFailed)?; let amount = ConvertBalance::convert_ref(amount).map_err(|_| MatchError::AmountToBalanceConversionFailed)?;
Ok((what, amount)) Ok((what, amount))
} }
} }
@@ -123,13 +82,13 @@ impl<
> MatchesFungibles<AssetId, Balance> for > MatchesFungibles<AssetId, Balance> for
ConvertedAbstractAssetId<AssetId, Balance, ConvertAssetId, ConvertBalance> ConvertedAbstractAssetId<AssetId, Balance, ConvertAssetId, ConvertBalance>
{ {
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), Error> { fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> {
let (id, amount) = match a { let (id, amount) = match a {
MultiAsset::AbstractFungible { id, amount } => (id, amount), MultiAsset::AbstractFungible { id, amount } => (id, amount),
_ => return Err(Error::AssetNotFound), _ => return Err(MatchError::AssetNotFound),
}; };
let what = ConvertAssetId::convert_ref(id).map_err(|_| Error::AssetIdConversionFailed)?; let what = ConvertAssetId::convert_ref(id).map_err(|_| MatchError::AssetIdConversionFailed)?;
let amount = ConvertBalance::convert_ref(amount).map_err(|_| Error::AmountToBalanceConversionFailed)?; let amount = ConvertBalance::convert_ref(amount).map_err(|_| MatchError::AmountToBalanceConversionFailed)?;
Ok((what, amount)) Ok((what, amount))
} }
} }
@@ -151,9 +110,9 @@ impl<
// Check we handle this asset. // Check we handle this asset.
let (asset_id, amount) = Matcher::matches_fungibles(what)?; let (asset_id, amount) = Matcher::matches_fungibles(what)?;
let source = AccountIdConverter::convert_ref(from) let source = AccountIdConverter::convert_ref(from)
.map_err(|()| Error::AccountIdConversionFailed)?; .map_err(|()| MatchError::AccountIdConversionFailed)?;
let dest = AccountIdConverter::convert_ref(to) let dest = AccountIdConverter::convert_ref(to)
.map_err(|()| Error::AccountIdConversionFailed)?; .map_err(|()| MatchError::AccountIdConversionFailed)?;
Assets::transfer(asset_id, &source, &dest, amount, true) Assets::transfer(asset_id, &source, &dest, amount, true)
.map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?;
Ok(what.clone().into()) Ok(what.clone().into())
@@ -208,7 +167,7 @@ impl<
// Check we handle this asset. // Check we handle this asset.
let (asset_id, amount) = Matcher::matches_fungibles(what)?; let (asset_id, amount) = Matcher::matches_fungibles(what)?;
let who = AccountIdConverter::convert_ref(who) let who = AccountIdConverter::convert_ref(who)
.map_err(|()| Error::AccountIdConversionFailed)?; .map_err(|()| MatchError::AccountIdConversionFailed)?;
Assets::mint_into(asset_id, &who, amount) Assets::mint_into(asset_id, &who, amount)
.map_err(|e| XcmError::FailedToTransactAsset(e.into())) .map_err(|e| XcmError::FailedToTransactAsset(e.into()))
} }
@@ -220,7 +179,7 @@ impl<
// Check we handle this asset. // Check we handle this asset.
let (asset_id, amount) = Matcher::matches_fungibles(what)?; let (asset_id, amount) = Matcher::matches_fungibles(what)?;
let who = AccountIdConverter::convert_ref(who) let who = AccountIdConverter::convert_ref(who)
.map_err(|()| Error::AccountIdConversionFailed)?; .map_err(|()| MatchError::AccountIdConversionFailed)?;
Assets::burn_from(asset_id, &who, amount) Assets::burn_from(asset_id, &who, amount)
.map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?;
Ok(what.clone().into()) Ok(what.clone().into())
+4
View File
@@ -14,6 +14,10 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. // along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! # XCM-Builder
//!
//! Types and helpers for *building* XCM configuration.
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
#[cfg(test)] #[cfg(test)]
@@ -23,7 +23,6 @@ use xcm::v0::{MultiLocation, NetworkId, Junction};
use xcm_executor::traits::{InvertLocation, Convert}; use xcm_executor::traits::{InvertLocation, Convert};
pub struct Account32Hash<Network, AccountId>(PhantomData<(Network, AccountId)>); pub struct Account32Hash<Network, AccountId>(PhantomData<(Network, AccountId)>);
impl< impl<
Network: Get<NetworkId>, Network: Get<NetworkId>,
AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone,
@@ -37,6 +36,8 @@ impl<
} }
} }
/// A [`MultiLocation`] consisting of a single `Parent` [`Junction`] will be converted to the
/// default value of `AccountId` (e.g. all zeros for `AccountId32`).
pub struct ParentIsDefault<AccountId>(PhantomData<AccountId>); pub struct ParentIsDefault<AccountId>(PhantomData<AccountId>);
impl< impl<
AccountId: Default + Eq + Clone, AccountId: Default + Eq + Clone,
@@ -81,7 +82,6 @@ impl<
} }
pub struct SiblingParachainConvertsVia<ParaId, AccountId>(PhantomData<(ParaId, AccountId)>); pub struct SiblingParachainConvertsVia<ParaId, AccountId>(PhantomData<(ParaId, AccountId)>);
impl< impl<
ParaId: From<u32> + Into<u32> + AccountIdConversion<AccountId>, ParaId: From<u32> + Into<u32> + AccountIdConversion<AccountId>,
AccountId: Clone, AccountId: Clone,
@@ -103,6 +103,7 @@ impl<
} }
} }
/// Extracts the `AccountId32` from the passed `location` if the network matches.
pub struct AccountId32Aliases<Network, AccountId>(PhantomData<(Network, AccountId)>); pub struct AccountId32Aliases<Network, AccountId>(PhantomData<(Network, AccountId)>);
impl< impl<
Network: Get<NetworkId>, Network: Get<NetworkId>,
@@ -142,7 +143,40 @@ impl<
} }
} }
/// Simple location inverter; give it this location's ancestry and it'll figure out the inverted location. /// Simple location inverter; give it this location's ancestry and it'll figure out the inverted
/// location.
///
/// # Example
/// ## Network Topology
/// ```txt
/// v Source
/// Relay -> Para 1 -> Account20
/// -> Para 2 -> Account32
/// ^ Target
/// ```
/// ```rust
/// # use frame_support::parameter_types;
/// # use xcm::v0::{MultiLocation::{self, *}, Junction::*, NetworkId::Any};
/// # use xcm_builder::LocationInverter;
/// # use xcm_executor::traits::InvertLocation;
/// # fn main() {
/// parameter_types!{
/// pub Ancestry: MultiLocation = X2(
/// Parachain(1),
/// AccountKey20 { network: Any, key: Default::default() },
/// );
/// }
///
/// let input = X4(Parent, Parent, Parachain(2), AccountId32 { network: Any, id: Default::default() });
/// let inverted = LocationInverter::<Ancestry>::invert_location(&input);
/// assert_eq!(inverted, X4(
/// Parent,
/// Parent,
/// Parachain(1),
/// AccountKey20 { network: Any, key: Default::default() },
/// ));
/// # }
/// ```
pub struct LocationInverter<Ancestry>(PhantomData<Ancestry>); pub struct LocationInverter<Ancestry>(PhantomData<Ancestry>);
impl<Ancestry: Get<MultiLocation>> InvertLocation for LocationInverter<Ancestry> { impl<Ancestry: Get<MultiLocation>> InvertLocation for LocationInverter<Ancestry> {
fn invert_location(location: &MultiLocation) -> MultiLocation { fn invert_location(location: &MultiLocation) -> MultiLocation {
@@ -160,3 +194,72 @@ impl<Ancestry: Get<MultiLocation>> InvertLocation for LocationInverter<Ancestry>
result result
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use frame_support::parameter_types;
use xcm::v0::{MultiLocation::*, Junction::*, NetworkId::Any};
fn account20() -> Junction {
AccountKey20 { network: Any, key: Default::default() }
}
fn account32() -> Junction {
AccountId32 { network: Any, id: Default::default() }
}
// Network Topology
// v Source
// Relay -> Para 1 -> SmartContract -> Account
// -> Para 2 -> Account
// ^ Target
//
// Inputs and outputs written as file paths:
//
// input location (source to target): ../../../para_2/account32_default
// ancestry (root to source): para_1/account20_default/account20_default
// =>
// output (target to source): ../../para_1/account20_default/account20_default
#[test]
fn inverter_works_in_tree() {
parameter_types!{
pub Ancestry: MultiLocation = X3(Parachain(1), account20(), account20());
}
let input = X5(Parent, Parent, Parent, Parachain(2), account32());
let inverted = LocationInverter::<Ancestry>::invert_location(&input);
assert_eq!(inverted, X5(Parent, Parent, Parachain(1), account20(), account20()));
}
// Network Topology
// v Source
// Relay -> Para 1 -> SmartContract -> Account
// ^ Target
#[test]
fn inverter_uses_ancestry_as_inverted_location() {
parameter_types!{
pub Ancestry: MultiLocation = X2(account20(), account20());
}
let input = X2(Parent, Parent);
let inverted = LocationInverter::<Ancestry>::invert_location(&input);
assert_eq!(inverted, X2(account20(), account20()));
}
// Network Topology
// v Source
// Relay -> Para 1 -> CollectivePallet -> Plurality
// ^ Target
#[test]
fn inverter_uses_only_child_on_missing_ancestry() {
parameter_types!{
pub Ancestry: MultiLocation = X1(PalletInstance(5));
}
let input = X2(Parent, Parent);
let inverted = LocationInverter::<Ancestry>::invert_location(&input);
assert_eq!(inverted, X2(PalletInstance(5), OnlyChild));
}
}
@@ -14,12 +14,35 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. // along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Various implementations for the `MatchesFungible` trait.
use sp_std::{marker::PhantomData, convert::TryFrom}; use sp_std::{marker::PhantomData, convert::TryFrom};
use sp_runtime::traits::CheckedConversion; use sp_runtime::traits::CheckedConversion;
use xcm::v0::{MultiAsset, MultiLocation}; use xcm::v0::{MultiAsset, MultiLocation};
use frame_support::traits::Get; use frame_support::traits::Get;
use xcm_executor::traits::MatchesFungible; use xcm_executor::traits::MatchesFungible;
/// Converts a `MultiAsset` into balance `B` if it is a concrete fungible with an id equal to that
/// given by `T`'s `Get`.
///
/// # Example
///
/// ```
/// use xcm::v0::{MultiAsset, MultiLocation, Junction};
/// use xcm_builder::IsConcrete;
/// use xcm_executor::traits::MatchesFungible;
///
/// frame_support::parameter_types! {
/// pub TargetLocation: MultiLocation = MultiLocation::X1(Junction::Parent);
/// }
///
/// # fn main() {
/// let id = MultiLocation::X1(Junction::Parent);
/// let asset = MultiAsset::ConcreteFungible { id, amount: 999u128 };
/// // match `asset` if it is a concrete asset in `TargetLocation`.
/// assert_eq!(<IsConcrete<TargetLocation> as MatchesFungible<u128>>::matches_fungible(&asset), Some(999));
/// # }
/// ```
pub struct IsConcrete<T>(PhantomData<T>); pub struct IsConcrete<T>(PhantomData<T>);
impl<T: Get<MultiLocation>, B: TryFrom<u128>> MatchesFungible<B> for IsConcrete<T> { impl<T: Get<MultiLocation>, B: TryFrom<u128>> MatchesFungible<B> for IsConcrete<T> {
fn matches_fungible(a: &MultiAsset) -> Option<B> { fn matches_fungible(a: &MultiAsset) -> Option<B> {
@@ -30,6 +53,26 @@ impl<T: Get<MultiLocation>, B: TryFrom<u128>> MatchesFungible<B> for IsConcrete<
} }
} }
} }
/// Same as [`IsConcrete`] but for a fungible with abstract location.
///
/// # Example
///
/// ```
/// use xcm::v0::{MultiAsset};
/// use xcm_builder::IsAbstract;
/// use xcm_executor::traits::MatchesFungible;
///
/// frame_support::parameter_types! {
/// pub TargetLocation: &'static [u8] = &[7u8];
/// }
///
/// # fn main() {
/// let asset = MultiAsset::AbstractFungible { id: vec![7u8], amount: 999u128 };
/// // match `asset` if it is a concrete asset in `TargetLocation`.
/// assert_eq!(<IsAbstract<TargetLocation> as MatchesFungible<u128>>::matches_fungible(&asset), Some(999));
/// # }
/// ```
pub struct IsAbstract<T>(PhantomData<T>); pub struct IsAbstract<T>(PhantomData<T>);
impl<T: Get<&'static [u8]>, B: TryFrom<u128>> MatchesFungible<B> for IsAbstract<T> { impl<T: Get<&'static [u8]>, B: TryFrom<u128>> MatchesFungible<B> for IsAbstract<T> {
fn matches_fungible(a: &MultiAsset) -> Option<B> { fn matches_fungible(a: &MultiAsset) -> Option<B> {
+13 -8
View File
@@ -36,8 +36,17 @@ pub use crate::{
FixedRateOfConcreteFungible, AllowKnownQueryResponses, LocationInverter, FixedRateOfConcreteFungible, AllowKnownQueryResponses, LocationInverter,
}; };
pub enum TestOrigin { Root, Relay, Signed(u64), Parachain(u32) } pub enum TestOrigin {
Root,
Relay,
Signed(u64),
Parachain(u32),
}
/// A dummy call.
///
/// Each item contains the amount of weight that it *wants* to consume as the first item, and the actual amount (if
/// different from the former) in the second option.
#[derive(Debug, Encode, Decode, Eq, PartialEq, Clone, Copy)] #[derive(Debug, Encode, Decode, Eq, PartialEq, Clone, Copy)]
pub enum TestCall { pub enum TestCall {
OnlyRoot(Weight, Option<Weight>), OnlyRoot(Weight, Option<Weight>),
@@ -60,17 +69,13 @@ impl Dispatchable for TestCall {
=> maybe_actual, => maybe_actual,
}; };
if match (&origin, &self) { if match (&origin, &self) {
(TestOrigin::Parachain(i), TestCall::OnlyParachain(_, _, Some(j))) (TestOrigin::Parachain(i), TestCall::OnlyParachain(_, _, Some(j))) => i == j,
=> i == j, (TestOrigin::Signed(i), TestCall::OnlySigned(_, _, Some(j))) => i == j,
(TestOrigin::Signed(i), TestCall::OnlySigned(_, _, Some(j)))
=> i == j,
(TestOrigin::Root, TestCall::OnlyRoot(..)) (TestOrigin::Root, TestCall::OnlyRoot(..))
| (TestOrigin::Parachain(_), TestCall::OnlyParachain(_, _, None)) | (TestOrigin::Parachain(_), TestCall::OnlyParachain(_, _, None))
| (TestOrigin::Signed(_), TestCall::OnlySigned(_, _, None)) | (TestOrigin::Signed(_), TestCall::OnlySigned(_, _, None))
| (_, TestCall::Any(..)) | (_, TestCall::Any(..))
=> true, => true,
_ => false, _ => false,
} { } {
Ok(post_info) Ok(post_info)
@@ -151,7 +156,7 @@ pub fn to_account(l: MultiLocation) -> Result<u64, MultiLocation> {
X1(Parachain(id)) => 1000 + id as u64, X1(Parachain(id)) => 1000 + id as u64,
// Self at 3000 // Self at 3000
Null => 3000, Null => 3000,
// Parent at 3000 // Parent at 3001
X1(Parent) => 3001, X1(Parent) => 3001,
l => return Err(l), l => return Err(l),
}) })
@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. // along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Various implementations for `ConvertOrigin`.
use sp_std::{marker::PhantomData, convert::TryInto}; use sp_std::{marker::PhantomData, convert::TryInto};
use xcm::v0::{MultiLocation, OriginKind, NetworkId, Junction, BodyId, BodyPart}; use xcm::v0::{MultiLocation, OriginKind, NetworkId, Junction, BodyId, BodyPart};
use xcm_executor::traits::{Convert, ConvertOrigin}; use xcm_executor::traits::{Convert, ConvertOrigin};
@@ -21,8 +23,7 @@ use frame_support::traits::{EnsureOrigin, Get, OriginTrait, GetBacking};
use frame_system::RawOrigin as SystemRawOrigin; use frame_system::RawOrigin as SystemRawOrigin;
use polkadot_parachain::primitives::IsSystem; use polkadot_parachain::primitives::IsSystem;
/// Sovereign accounts use the system's `Signed` origin with an account ID derived from the /// Sovereign accounts use the system's `Signed` origin with an account ID derived from the `LocationConverter`.
/// `LocationConverter`.
pub struct SovereignSignedViaLocation<LocationConverter, Origin>( pub struct SovereignSignedViaLocation<LocationConverter, Origin>(
PhantomData<(LocationConverter, Origin)> PhantomData<(LocationConverter, Origin)>
); );
+3 -2
View File
@@ -28,8 +28,8 @@ use xcm::v0::{
pub mod traits; pub mod traits;
use traits::{ use traits::{
TransactAsset, ConvertOrigin, FilterAssetLocation, InvertLocation, WeightBounds, WeightTrader, ShouldExecute, TransactAsset, ConvertOrigin, FilterAssetLocation, InvertLocation, WeightBounds, WeightTrader,
OnResponse ShouldExecute, OnResponse
}; };
mod assets; mod assets;
@@ -37,6 +37,7 @@ pub use assets::{Assets, AssetId};
mod config; mod config;
pub use config::Config; pub use config::Config;
/// The XCM executor.
pub struct XcmExecutor<Config>(PhantomData<Config>); pub struct XcmExecutor<Config>(PhantomData<Config>);
impl<Config: config::Config> ExecuteXcm<Config::Call> for XcmExecutor<Config> { impl<Config: config::Config> ExecuteXcm<Config::Call> for XcmExecutor<Config> {
@@ -24,6 +24,9 @@ use xcm::v0::{MultiLocation, OriginKind};
/// One of `convert`/`convert_ref` and `reverse`/`reverse_ref` MUST be implemented. If possible, implement /// One of `convert`/`convert_ref` and `reverse`/`reverse_ref` MUST be implemented. If possible, implement
/// `convert_ref`, since this will never result in a clone. Use `convert` when you definitely need to consume /// `convert_ref`, since this will never result in a clone. Use `convert` when you definitely need to consume
/// the source value. /// the source value.
///
/// Can be amalgamated into tuples. If any of the tuple elements converts into `Ok(_)` it short circuits. Otherwise returns
/// the `Err(_)` of the last failing conversion (or `Err(())` for ref conversions).
pub trait Convert<A: Clone, B: Clone> { pub trait Convert<A: Clone, B: Clone> {
/// Convert from `value` (of type `A`) into an equivalent value of type `B`, `Err` if not possible. /// Convert from `value` (of type `A`) into an equivalent value of type `B`, `Err` if not possible.
fn convert(value: A) -> Result<B, A> { Self::convert_ref(&value).map_err(|_| value) } fn convert(value: A) -> Result<B, A> { Self::convert_ref(&value).map_err(|_| value) }
@@ -115,7 +118,49 @@ impl<T: Clone + Encode + Decode> Convert<Vec<u8>, T> for Decoded {
fn reverse_ref(value: impl Borrow<T>) -> Result<Vec<u8>, ()> { Ok(value.borrow().encode()) } fn reverse_ref(value: impl Borrow<T>) -> Result<Vec<u8>, ()> { Ok(value.borrow().encode()) }
} }
/// A convertor trait for origin types.
///
/// Can be amalgamated into tuples. If any of the tuple elements returns `Ok(_)`, it short circuits. Else, the `Err(_)`
/// of the last tuple item is returned. Each intermediate `Err(_)` might return a different `origin` of type `Origin`
/// which is passed to the next convert item.
///
/// ```rust
/// # use xcm::v0::{MultiLocation, Junction, OriginKind};
/// # use xcm_executor::traits::ConvertOrigin;
/// // A convertor that will bump the para id and pass it to the next one.
/// struct BumpParaId;
/// impl ConvertOrigin<u32> for BumpParaId {
/// fn convert_origin(origin: MultiLocation, _: OriginKind) -> Result<u32, MultiLocation> {
/// match origin {
/// MultiLocation::X1(Junction::Parachain(id)) => {
/// Err(MultiLocation::X1(Junction::Parachain(id + 1)))
/// }
/// _ => unreachable!()
/// }
/// }
/// }
///
/// struct AcceptPara7;
/// impl ConvertOrigin<u32> for AcceptPara7 {
/// fn convert_origin(origin: MultiLocation, _: OriginKind) -> Result<u32, MultiLocation> {
/// match origin {
/// MultiLocation::X1(Junction::Parachain(id)) if id == 7 => {
/// Ok(7)
/// }
/// _ => Err(origin)
/// }
/// }
/// }
/// # fn main() {
/// let origin = MultiLocation::X1(Junction::Parachain(6));
/// assert!(
/// <(BumpParaId, AcceptPara7) as ConvertOrigin<u32>>::convert_origin(origin, OriginKind::Native)
/// .is_ok()
/// );
/// # }
/// ```
pub trait ConvertOrigin<Origin> { pub trait ConvertOrigin<Origin> {
/// Attempt to convert `origin` to the generic `Origin` whilst consuming it.
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result<Origin, MultiLocation>; fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result<Origin, MultiLocation>;
} }
@@ -123,14 +168,17 @@ pub trait ConvertOrigin<Origin> {
impl<O> ConvertOrigin<O> for Tuple { impl<O> ConvertOrigin<O> for Tuple {
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result<O, MultiLocation> { fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result<O, MultiLocation> {
for_tuples!( #( for_tuples!( #(
let origin = match Tuple::convert_origin(origin, kind) { Err(o) => o, r => return r }; let origin = match Tuple::convert_origin(origin, kind) {
Err(o) => o,
r => return r
};
)* ); )* );
Err(origin) Err(origin)
} }
} }
/// Means of inverting a location: given a location which describes a `target` interpreted from the `source`, this /// Means of inverting a location: given a location which describes a `target` interpreted from the
/// will provide the corresponding location which describes the `source` /// `source`, this will provide the corresponding location which describes the `source`.
pub trait InvertLocation { pub trait InvertLocation {
fn invert_location(l: &MultiLocation) -> MultiLocation; fn invert_location(l: &MultiLocation) -> MultiLocation;
} }
@@ -16,6 +16,9 @@
use xcm::v0::{MultiAsset, MultiLocation}; use xcm::v0::{MultiAsset, MultiLocation};
/// Filters assets/location pairs.
///
/// Can be amalgamated into tuples. If any item returns `true`, it short-circuits, else `false` is returned.
pub trait FilterAssetLocation { pub trait FilterAssetLocation {
/// A filter to distinguish between asset/location pairs. /// A filter to distinguish between asset/location pairs.
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool; fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool;
@@ -0,0 +1,59 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use xcm::v0::{MultiAsset, Error as XcmError};
use sp_std::result;
/// Errors associated with [`MatchesFungibles`] operation.
pub enum Error {
/// Asset not found.
AssetNotFound,
/// `MultiLocation` to `AccountId` conversion failed.
AccountIdConversionFailed,
/// `u128` amount to currency `Balance` conversion failed.
AmountToBalanceConversionFailed,
/// `MultiLocation` to `AssetId` conversion failed.
AssetIdConversionFailed,
}
impl From<Error> for XcmError {
fn from(e: Error) -> Self {
use XcmError::FailedToTransactAsset;
match e {
Error::AssetNotFound => FailedToTransactAsset("AssetNotFound"),
Error::AccountIdConversionFailed => FailedToTransactAsset("AccountIdConversionFailed"),
Error::AmountToBalanceConversionFailed => FailedToTransactAsset("AmountToBalanceConversionFailed"),
Error::AssetIdConversionFailed => FailedToTransactAsset("AssetIdConversionFailed"),
}
}
}
pub trait MatchesFungibles<AssetId, Balance> {
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), Error>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<
AssetId: Clone,
Balance: Clone,
> MatchesFungibles<AssetId, Balance> for Tuple {
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), Error> {
for_tuples!( #(
match Tuple::matches_fungibles(a) { o @ Ok(_) => return o, _ => () }
)* );
Err(Error::AssetNotFound)
}
}
@@ -22,6 +22,8 @@ mod filter_asset_location;
pub use filter_asset_location::{FilterAssetLocation}; pub use filter_asset_location::{FilterAssetLocation};
mod matches_fungible; mod matches_fungible;
pub use matches_fungible::{MatchesFungible}; pub use matches_fungible::{MatchesFungible};
mod matches_fungibles;
pub use matches_fungibles::{MatchesFungibles, Error};
mod on_response; mod on_response;
pub use on_response::OnResponse; pub use on_response::OnResponse;
mod should_execute; mod should_execute;
@@ -17,8 +17,11 @@
use xcm::v0::{Response, MultiLocation}; use xcm::v0::{Response, MultiLocation};
use frame_support::weights::Weight; use frame_support::weights::Weight;
/// Define what needs to be done upon receiving a query response.
pub trait OnResponse { pub trait OnResponse {
/// Returns `true` if we are expecting a response from `origin` for query `query_id`.
fn expecting_response(origin: &MultiLocation, query_id: u64) -> bool; fn expecting_response(origin: &MultiLocation, query_id: u64) -> bool;
/// Handler for receiving a `response` from `origin` relating to `query_id`.
fn on_response(origin: MultiLocation, query_id: u64, response: Response) -> Weight; fn on_response(origin: MultiLocation, query_id: u64, response: Response) -> Weight;
} }
impl OnResponse for () { impl OnResponse for () {
@@ -19,18 +19,21 @@ use xcm::v0::{Xcm, MultiLocation};
use frame_support::weights::Weight; use frame_support::weights::Weight;
/// Trait to determine whether the execution engine should actually execute a given XCM. /// Trait to determine whether the execution engine should actually execute a given XCM.
///
/// Can be amalgamated into a tuple to have multiple trials. If any of the tuple elements returns `Ok()`, the
/// execution stops. Else, `Err(_)` is returned if all elements reject the message.
pub trait ShouldExecute { pub trait ShouldExecute {
/// Returns `true` if the given `message` may be executed. /// Returns `true` if the given `message` may be executed.
/// ///
/// - `origin`: The origin (sender) of the message. /// - `origin`: The origin (sender) of the message.
/// - `top_level`: `true`` indicates the initial XCM coming from the `origin`, `false` indicates an embedded /// - `top_level`: `true` indicates the initial XCM coming from the `origin`, `false` indicates an embedded XCM
/// XCM executed internally as part of another message or an `Order`. /// executed internally as part of another message or an `Order`.
/// - `message`: The message itself. /// - `message`: The message itself.
/// - `shallow_weight`: The weight of the non-negotiable execution of the message. This does not include any /// - `shallow_weight`: The weight of the non-negotiable execution of the message. This does not include any
/// embedded XCMs sat behind mechanisms like `BuyExecution` which would need to answer for their own weight. /// embedded XCMs sat behind mechanisms like `BuyExecution` which would need to answer for their own weight.
/// - `weight_credit`: The pre-established amount of weight that the system has determined this message /// - `weight_credit`: The pre-established amount of weight that the system has determined this message may utilise
/// may utilise in its execution. Typically non-zero only because of prior fee payment, but could /// in its execution. Typically non-zero only because of prior fee payment, but could in principle be due to other
/// in principle be due to other factors. /// factors.
fn should_execute<Call>( fn should_execute<Call>(
origin: &MultiLocation, origin: &MultiLocation,
top_level: bool, top_level: bool,
@@ -51,7 +54,7 @@ impl ShouldExecute for Tuple {
) -> Result<(), ()> { ) -> Result<(), ()> {
for_tuples!( #( for_tuples!( #(
match Tuple::should_execute(origin, top_level, message, shallow_weight, weight_credit) { match Tuple::should_execute(origin, top_level, message, shallow_weight, weight_credit) {
o @ Ok(()) => return o, Ok(()) => return Ok(()),
_ => (), _ => (),
} }
)* ); )* );
@@ -20,9 +20,11 @@ use crate::Assets;
/// Facility for asset transacting. /// Facility for asset transacting.
/// ///
/// This should work with as many asset/location combinations as possible. Locations to support may include non- /// This should work with as many asset/location combinations as possible. Locations to support may include non-account
/// account locations such as a `MultiLocation::X1(Junction::Parachain)`. Different chains may handle them in /// locations such as a `MultiLocation::X1(Junction::Parachain)`. Different chains may handle them in different ways.
/// different ways. ///
/// Can be amalgamated as a tuple of items that implement this trait. In such executions, if any of the transactors
/// returns `Ok(())`, then it will short circuit. Else, execution is passed to the next transactor.
pub trait TransactAsset { pub trait TransactAsset {
/// Ensure that `check_in` will result in `Ok`. /// Ensure that `check_in` will result in `Ok`.
/// ///