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
+3 -2
View File
@@ -28,8 +28,8 @@ use xcm::v0::{
pub mod traits;
use traits::{
TransactAsset, ConvertOrigin, FilterAssetLocation, InvertLocation, WeightBounds, WeightTrader, ShouldExecute,
OnResponse
TransactAsset, ConvertOrigin, FilterAssetLocation, InvertLocation, WeightBounds, WeightTrader,
ShouldExecute, OnResponse
};
mod assets;
@@ -37,6 +37,7 @@ pub use assets::{Assets, AssetId};
mod config;
pub use config::Config;
/// The XCM executor.
pub struct XcmExecutor<Config>(PhantomData<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
/// `convert_ref`, since this will never result in a clone. Use `convert` when you definitely need to consume
/// 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> {
/// 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) }
@@ -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()) }
}
/// 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> {
/// Attempt to convert `origin` to the generic `Origin` whilst consuming it.
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result<Origin, MultiLocation>;
}
@@ -123,14 +168,17 @@ pub trait ConvertOrigin<Origin> {
impl<O> ConvertOrigin<O> for Tuple {
fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result<O, MultiLocation> {
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)
}
}
/// Means of inverting a location: given a location which describes a `target` interpreted from the `source`, this
/// will provide the corresponding location which describes the `source`
/// Means of inverting a location: given a location which describes a `target` interpreted from the
/// `source`, this will provide the corresponding location which describes the `source`.
pub trait InvertLocation {
fn invert_location(l: &MultiLocation) -> MultiLocation;
}
@@ -16,6 +16,9 @@
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 {
/// A filter to distinguish between asset/location pairs.
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};
mod matches_fungible;
pub use matches_fungible::{MatchesFungible};
mod matches_fungibles;
pub use matches_fungibles::{MatchesFungibles, Error};
mod on_response;
pub use on_response::OnResponse;
mod should_execute;
@@ -17,8 +17,11 @@
use xcm::v0::{Response, MultiLocation};
use frame_support::weights::Weight;
/// Define what needs to be done upon receiving a query response.
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;
/// Handler for receiving a `response` from `origin` relating to `query_id`.
fn on_response(origin: MultiLocation, query_id: u64, response: Response) -> Weight;
}
impl OnResponse for () {
@@ -19,18 +19,21 @@ use xcm::v0::{Xcm, MultiLocation};
use frame_support::weights::Weight;
/// 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 {
/// Returns `true` if the given `message` may be executed.
///
/// - `origin`: The origin (sender) of the message.
/// - `top_level`: `true`` indicates the initial XCM coming from the `origin`, `false` indicates an embedded
/// XCM executed internally as part of another message or an `Order`.
/// - `top_level`: `true` indicates the initial XCM coming from the `origin`, `false` indicates an embedded XCM
/// executed internally as part of another message or an `Order`.
/// - `message`: The message itself.
/// - `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.
/// - `weight_credit`: The pre-established amount of weight that the system has determined this message
/// may utilise in its execution. Typically non-zero only because of prior fee payment, but could
/// in principle be due to other factors.
/// - `weight_credit`: The pre-established amount of weight that the system has determined this message may utilise
/// in its execution. Typically non-zero only because of prior fee payment, but could in principle be due to other
/// factors.
fn should_execute<Call>(
origin: &MultiLocation,
top_level: bool,
@@ -51,7 +54,7 @@ impl ShouldExecute for Tuple {
) -> Result<(), ()> {
for_tuples!( #(
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.
///
/// This should work with as many asset/location combinations as possible. Locations to support may include non-
/// account locations such as a `MultiLocation::X1(Junction::Parachain)`. Different chains may handle them in
/// different ways.
/// This should work with as many asset/location combinations as possible. Locations to support may include non-account
/// locations such as a `MultiLocation::X1(Junction::Parachain)`. Different chains may handle them in 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 {
/// Ensure that `check_in` will result in `Ok`.
///