From 3e15173b17ef4b7c99963fbc818344ff9878884f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 4 Apr 2021 17:51:31 +0200 Subject: [PATCH 01/84] MultiAsset TWO --- xcm/src/v0/mod.rs | 2 + xcm/src/v0/multiasset.rs | 128 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 xcm/src/v0/multiasset.rs diff --git a/xcm/src/v0/mod.rs b/xcm/src/v0/mod.rs index c69093d4f851..7fef83987d94 100644 --- a/xcm/src/v0/mod.rs +++ b/xcm/src/v0/mod.rs @@ -27,6 +27,8 @@ mod multi_asset; mod multi_location; mod order; mod traits; +pub mod multiasset; // the new multiasset. + pub use junction::{Junction, NetworkId}; pub use multi_asset::{MultiAsset, AssetInstance}; pub use multi_location::MultiLocation; diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs new file mode 100644 index 000000000000..8e1839719f09 --- /dev/null +++ b/xcm/src/v0/multiasset.rs @@ -0,0 +1,128 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Substrate 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. + +// Substrate 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 Cumulus. If not, see . + +//! Cross-Consensus Message format asset data structure. + +use alloc::vec::Vec; +use parity_scale_codec::{self as codec, Encode, Decode}; +use super::{MultiLocation, multi_asset::{AssetInstance, MultiAsset as OldMultiAsset}}; + +/// Classification of an asset being concrete or abstract. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +pub enum AssetId { + Concrete(MultiLocation), + Abstract(Vec), +} + +impl AssetId { + /// Prepend a MultiLocation to a concrete asset, giving it a new root location. + pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { + if let AssetId::Concrete(ref mut l) = self { + l.prepend_with(prepend.clone()).map_err(|_| ())?; + } + Ok(()) + } +} + +/// Classification of whether an asset is fungible or not, along with an optional amount or instance. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +pub enum Fungibility { + Fungible(Option), + NonFungible(Option), +} + +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +pub enum MultiAsset { + None, + Asset(Fungibility, Option), + All, +} + +impl From<(AssetId, Fungibility)> for MultiAsset { + fn from((asset_id, fungibility): (AssetId, Fungibility)) -> MultiAsset { + MultiAsset::Asset(fungibility, Some(asset_id)) + } +} + +impl From for MultiAsset { + fn from(fungibility: Fungibility) -> MultiAsset { + MultiAsset::Asset(fungibility, None) + } +} + +impl From<()> for MultiAsset { + fn from(_: ()) -> MultiAsset { + MultiAsset::None + } +} + +impl Encode for MultiAsset { + fn encode(&self) -> Vec { + OldMultiAsset::from(self.clone()).encode() + } +} + +impl Decode for MultiAsset { + fn decode(input: &mut I) -> Result { + OldMultiAsset::decode(input).map(Into::into) + } +} + +impl From for OldMultiAsset { + fn from(a: MultiAsset) -> Self { + use {AssetId::*, Fungibility::*, OldMultiAsset::*, MultiAsset::Asset}; + match a { + MultiAsset::None => OldMultiAsset::None, + MultiAsset::All => All, + + Asset(Fungible(_), Option::None) => AllFungible, + Asset(NonFungible(_), Option::None) => AllNonFungible, + + Asset(Fungible(Option::None), Some(Concrete(id))) => AllConcreteFungible { id }, + Asset(Fungible(Option::None), Some(Abstract(id))) => AllAbstractFungible { id }, + Asset(NonFungible(Option::None), Some(Concrete(class))) => AllConcreteNonFungible { class }, + Asset(NonFungible(Option::None), Some(Abstract(class))) => AllAbstractNonFungible { class }, + + Asset(Fungible(Some(amount)), Some(Concrete(id))) => ConcreteFungible { id, amount }, + Asset(Fungible(Some(amount)), Some(Abstract(id))) => AbstractFungible { id, amount }, + Asset(NonFungible(Some(instance)), Some(Concrete(class))) => ConcreteNonFungible { class, instance }, + Asset(NonFungible(Some(instance)), Some(Abstract(class))) => AbstractNonFungible { class, instance }, + } + } +} + +impl From for MultiAsset { + fn from(a: OldMultiAsset) -> Self { + use {AssetId::*, Fungibility::*, OldMultiAsset::*, MultiAsset::Asset}; + match a { + None => MultiAsset::None, + All => MultiAsset::All, + + AllFungible => Asset(Fungible(Option::None), Option::None), + AllNonFungible => Asset(NonFungible(Option::None), Option::None), + + AllConcreteFungible { id } => Asset(Fungible(Option::None), Some(Concrete(id))), + AllAbstractFungible { id } => Asset(Fungible(Option::None), Some(Abstract(id))), + AllConcreteNonFungible { class } => Asset(NonFungible(Option::None), Some(Concrete(class))), + AllAbstractNonFungible { class } => Asset(NonFungible(Option::None), Some(Abstract(class))), + + ConcreteFungible { id, amount } => Asset(Fungible(Some(amount)), Some(Concrete(id))), + AbstractFungible { id, amount } => Asset(Fungible(Some(amount)), Some(Abstract(id))), + ConcreteNonFungible { class, instance } => Asset(NonFungible(Some(instance)), Some(Concrete(class))), + AbstractNonFungible { class, instance } => Asset(NonFungible(Some(instance)), Some(Abstract(class))), + } + } +} From ee80e03f413a2a2076759a3fd0313daacc391347 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 30 Jul 2021 19:16:58 +0200 Subject: [PATCH 02/84] Draft next MultiAsset API. --- xcm/src/lib.rs | 15 + xcm/src/v0/mod.rs | 6 +- xcm/src/v0/multi_asset.rs | 43 +-- xcm/src/v0/multiasset.rs | 440 ++++++++++++++++++++++--- xcm/src/v0/order.rs | 21 +- xcm/xcm-builder/src/mock.rs | 2 +- xcm/xcm-executor/src/assets.rs | 577 +++++++++++---------------------- xcm/xcm-executor/src/lib.rs | 14 +- 8 files changed, 615 insertions(+), 503 deletions(-) diff --git a/xcm/src/lib.rs b/xcm/src/lib.rs index 1addc44bd552..8b459adc7185 100644 --- a/xcm/src/lib.rs +++ b/xcm/src/lib.rs @@ -63,3 +63,18 @@ pub enum VersionedMultiLocation { pub enum VersionedMultiAsset { V0(v0::MultiAsset), } + +impl From for VersionedMultiAsset { + fn from(x: v0::MultiAsset) -> Self { + VersionedMultiAsset::V0(x) + } +} + +impl core::convert::TryFrom for v0::MultiAsset { + type Error = (); + fn try_from(x: VersionedMultiAsset) -> core::result::Result { + match x { + VersionedMultiAsset::V0(x) => Ok(x), + } + } +} diff --git a/xcm/src/v0/mod.rs b/xcm/src/v0/mod.rs index 619e9b9bc75a..cc8044ea19d2 100644 --- a/xcm/src/v0/mod.rs +++ b/xcm/src/v0/mod.rs @@ -20,7 +20,7 @@ use core::{result, convert::TryFrom, fmt::Debug}; use derivative::Derivative; use alloc::vec::Vec; use parity_scale_codec::{self, Encode, Decode}; -use crate::{VersionedMultiAsset, DoubleEncoded, VersionedXcm}; +use crate::{VersionedMultiAsset, VersionedWildMultiAsset, DoubleEncoded, VersionedXcm}; mod junction; mod multi_asset; @@ -30,7 +30,9 @@ mod traits; pub mod multiasset; // the new multiasset. pub use junction::{Junction, NetworkId, BodyId, BodyPart}; -pub use multi_asset::{MultiAsset, AssetInstance}; +pub use multiasset::{ + AssetId, AssetInstance, MultiAsset, MultiAssets, MultiAssetFilter, Fungibility, WildMultiAsset, WildFungibility +}; pub use multi_location::MultiLocation; pub use order::Order; pub use traits::{Error, Result, SendXcm, ExecuteXcm, Outcome}; diff --git a/xcm/src/v0/multi_asset.rs b/xcm/src/v0/multi_asset.rs index dc682902df65..d7065d3e1d8e 100644 --- a/xcm/src/v0/multi_asset.rs +++ b/xcm/src/v0/multi_asset.rs @@ -20,33 +20,7 @@ use core::{result, convert::TryFrom}; use alloc::vec::Vec; use parity_scale_codec::{self, Encode, Decode}; -use super::{MultiLocation, VersionedMultiAsset}; - -/// A general identifier for an instance of a non-fungible asset class. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)] -pub enum AssetInstance { - /// Undefined - used if the NFA class has only one instance. - Undefined, - - /// A compact index. Technically this could be greater than `u128`, but this implementation supports only - /// values up to `2**128 - 1`. - Index { #[codec(compact)] id: u128 }, - - /// A 4-byte fixed-length datum. - Array4([u8; 4]), - - /// An 8-byte fixed-length datum. - Array8([u8; 8]), - - /// A 16-byte fixed-length datum. - Array16([u8; 16]), - - /// A 32-byte fixed-length datum. - Array32([u8; 32]), - - /// An arbitrary piece of data. Use only when necessary. - Blob(Vec), -} +use super::{MultiLocation, VersionedMultiAsset, AssetInstance}; /// A single general identifier for an asset. /// @@ -306,21 +280,6 @@ impl MultiAsset { } } -impl From for VersionedMultiAsset { - fn from(x: MultiAsset) -> Self { - VersionedMultiAsset::V0(x) - } -} - -impl TryFrom for MultiAsset { - type Error = (); - fn try_from(x: VersionedMultiAsset) -> result::Result { - match x { - VersionedMultiAsset::V0(x) => Ok(x), - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index 8e1839719f09..010f1ce4c1da 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -12,13 +12,51 @@ // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . +// along with Polkadot. If not, see . -//! Cross-Consensus Message format asset data structure. +//! Cross-Consensus Message format asset data structures. +//! +//! This encompasses four types for repesenting assets: +//! - `MultiAsset`: A description of a single asset, either an instance of a non-fungible or some amount of a fungible. +//! - `MultiAssets`: A collection of `MultiAsset`s. These are stored in a `Vec` and sorted with fungibles first. +//! - `Wild`: A single asset wildcard, this can either be "all" assets, or all assets of a specific kind. +//! - `MultiAssetFilter`: A combination of `Wild` and `MultiAssets` designed for efficiently filtering an XCM holding +//! account. +use core::convert::{TryFrom, TryInto}; use alloc::vec::Vec; use parity_scale_codec::{self as codec, Encode, Decode}; -use super::{MultiLocation, multi_asset::{AssetInstance, MultiAsset as OldMultiAsset}}; +use super::{ + MultiLocation, multi_asset::MultiAsset as OldMultiAsset, VersionedMultiAsset, + VersionedWildMultiAsset, +}; +use core::cmp::Ordering; + +/// A general identifier for an instance of a non-fungible asset class. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)] +pub enum AssetInstance { + /// Undefined - used if the NFA class has only one instance. + Undefined, + + /// A compact index. Technically this could be greater than `u128`, but this implementation supports only + /// values up to `2**128 - 1`. + Index { #[codec(compact)] id: u128 }, + + /// A 4-byte fixed-length datum. + Array4([u8; 4]), + + /// An 8-byte fixed-length datum. + Array8([u8; 8]), + + /// A 16-byte fixed-length datum. + Array16([u8; 16]), + + /// A 32-byte fixed-length datum. + Array32([u8; 32]), + + /// An arbitrary piece of data. Use only when necessary. + Blob(Vec), +} /// Classification of an asset being concrete or abstract. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] @@ -28,44 +66,126 @@ pub enum AssetId { } impl AssetId { - /// Prepend a MultiLocation to a concrete asset, giving it a new root location. + /// Prepend a `MultiLocation` to a concrete asset, giving it a new root location. pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { if let AssetId::Concrete(ref mut l) = self { l.prepend_with(prepend.clone()).map_err(|_| ())?; } Ok(()) } + + /// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `MultiAsset` value. + pub fn into_multiasset(self, fun: Fungibility) -> MultiAsset { + MultiAsset { fun, id: self } + } + + /// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `WildMultiAsset` + /// definite (`Asset`) value. + pub fn into_wild(self, fun: Fungibility) -> WildMultiAsset { + WildMultiAsset::Asset(fun, self) + } + + /// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `WildMultiAsset` + /// wildcard (`AllOf`) value. + pub fn into_allof(self, fun: WildFungibility) -> WildMultiAsset { + WildMultiAsset::AllOf(fun, self) + } } -/// Classification of whether an asset is fungible or not, along with an optional amount or instance. +/// Classification of whether an asset is fungible or not, along with an mandatory amount or instance. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] pub enum Fungibility { - Fungible(Option), - NonFungible(Option), + Fungible(u128), + NonFungible(AssetInstance), } -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] -pub enum MultiAsset { - None, - Asset(Fungibility, Option), - All, +impl Fungibility { + pub fn is_kind(&self, w: WildFungibility) -> bool { + use {Fungibility::*, WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible}}; + matches!((self, w), (Fungible(_), WildFungible) | (NonFungible(_), WildNonFungible)) + } +} + +impl From for Fungibility { + fn from(amount: u128) -> Fungibility { + debug_assert_ne!(amount, 0); + Fungibility::Fungible(amount) + } +} + +impl From for Fungibility { + fn from(instance: AssetInstance) -> Fungibility { + Fungibility::NonFungible(instance) + } +} + + + + +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct MultiAsset { + pub id: AssetId, + pub fun: Fungibility, +} + +impl PartialOrd for MultiAsset { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for MultiAsset { + fn cmp(&self, other: &Self) -> Ordering { + match (&self.fun, &other.fun) { + (Fungibility::Fungible(..), Fungibility::NonFungible(..)) => return Ordering::Less, + (Fungibility::NonFungible(..), Fungibility::Fungible(..)) => return Ordering::Greater, + } + (&self.id, &self.fun).cmp((&other.id, &other.fun)) + } } impl From<(AssetId, Fungibility)> for MultiAsset { - fn from((asset_id, fungibility): (AssetId, Fungibility)) -> MultiAsset { - MultiAsset::Asset(fungibility, Some(asset_id)) + fn from((id, fun): (AssetId, Fungibility)) -> MultiAsset { + MultiAsset { fun, id } } } -impl From for MultiAsset { - fn from(fungibility: Fungibility) -> MultiAsset { - MultiAsset::Asset(fungibility, None) +impl From<(AssetId, u128)> for MultiAsset { + fn from((id, amount): (AssetId, u128)) -> MultiAsset { + MultiAsset { fun: amount.into(), id } + } +} + +impl From<(AssetId, AssetInstance)> for MultiAsset { + fn from((id, instance): (AssetId, AssetInstance)) -> MultiAsset { + MultiAsset { fun: instance.into(), id } + } +} + +impl From for OldMultiAsset { + fn from(a: MultiAsset) -> Self { + use {AssetId::*, Fungibility::*, OldMultiAsset::*}; + match (a.fun, a.id) { + (Fungible(amount), Concrete(id)) => ConcreteFungible { id, amount }, + (Fungible(amount), Abstract(id)) => AbstractFungible { id, amount }, + (NonFungible(instance), Concrete(class)) => ConcreteNonFungible { class, instance }, + (NonFungible(instance), Abstract(class)) => AbstractNonFungible { class, instance }, + } } } -impl From<()> for MultiAsset { - fn from(_: ()) -> MultiAsset { - MultiAsset::None +impl TryFrom for MultiAsset { + type Error = (); + fn try_from(a: OldMultiAsset) -> Result { + use {AssetId::*, Fungibility::*, OldMultiAsset::*}; + let (fun, id) = match a { + ConcreteFungible { id, amount } if amount > 0 => (Fungible(amount), Concrete(id)), + AbstractFungible { id, amount } if amount > 0 => (Fungible(amount), Abstract(id)), + ConcreteNonFungible { class, instance } => (NonFungible(instance), Concrete(class)), + AbstractNonFungible { class, instance } => (NonFungible(instance), Abstract(class)), + _ => return Err(()), + }; + Ok(MultiAsset { fun, id }) } } @@ -76,53 +196,273 @@ impl Encode for MultiAsset { } impl Decode for MultiAsset { + fn decode(input: &mut I) -> Result { + OldMultiAsset::decode(input) + .and_then(|r| TryInto::try_into(r).map_err(|_| "Unsupported wildcard".into()))? + } +} + +impl MultiAsset { + fn is_fungible(&self, maybe_id: Option) -> bool { + use Fungibility::*; + matches!(self.fun, Fungible(..)) && maybe_id.map_or(true, |i| i == self.id) + } + + fn is_non_fungible(&self, maybe_id: Option) -> bool { + use Fungibility::*; + matches!(self.fun, NonFungible(..)) && maybe_id.map_or(true, |i| i == self.id) + } + + /// Prepend a `MultiLocation` to a concrete asset, giving it a new root location. + pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { + self.id.reanchor(prepend) + } + + /// Returns true if `self` is a super-set of the given `inner`. + pub fn contains(&self, inner: &MultiAsset) -> bool { + use {MultiAsset::*, Fungibility::*}; + if self.id == inner.id { + match (&self.fun, &inner.fun) { + (Fungible(a), Fungible(i)) if a >= i => return true, + (NonFungible(a), NonFungible(i)) if a == i => return true, + _ => (), + } + } + false + } +} + + + +/// A vec of MultiAssets. There may be no duplicate fungible items in here and when decoding, they must be sorted. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode)] +pub struct MultiAssets(Vec); + +impl Decode for MultiAssets { + fn decode(input: &mut I) -> Result { + let r = Vec::::decode(input)?; + if r.is_empty() { return Ok(Self(Vec::new())) } + r.iter().skip(1).try_fold(&r[0], |a, b| { + if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) { + Ok(b) + } else { + Err("Unsupported wildcard".into()) + } + })?; + Ok(Self(r)) + } +} + +impl From for Vec { + fn from(a: MultiAssets) -> Self { + a.0.into_iter().map(OldMultiAsset::from).collect() + } +} + +impl TryFrom> for MultiAssets { + type Error = (); + fn try_from(a: Vec) -> Result { + a.0.into_iter().map(MultiAsset::try_from).collect() + } +} + +impl MultiAssets { + /// A new (empty) value. + pub fn new() -> Self { + Self(Vec::new()) + } + + /// Add some asset onto the multiasset list. This is quite a laborious operation since it maintains the ordering. + pub fn push(&mut self, a: MultiAsset) { + if let Fungibility::Fungible(ref amount) = a.fun { + for asset in self.0.iter_mut().filter(|x| x.id == a.id) { + if let Fungibility::Fungible(ref mut balance) = asset.fun { + *balance += *amount; + return + } + } + } + self.0.push(a); + self.0.sort(); + } + + /// Returns `true` if this definitely represents no asset. + pub fn is_none(&self) -> bool { + self.0.is_empty() + } + + /// Returns true if `self` is a super-set of the given `inner`. + pub fn contains(&self, inner: &MultiAsset) -> bool { + use {MultiAsset::*, Fungibility::*}; + self.0.iter().any(|i| i.contains(inner)) + } + + /// Consume `self` and return the inner vec. + pub fn drain(self) -> Vec { + self.0 + } + + /// Return a reference to the inner vec. + pub fn inner(&self) -> &Vec { + &self.0 + } + + /// Prepend a `MultiLocation` to any concrete asset items, giving it a new root location. + pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { + self.0.iter_mut().try_for_each(|i| i.reanchor(prepend)) + } +} + + + + + +/// Classification of whether an asset is fungible or not, along with an optional amount or instance. +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +pub enum WildFungibility { + Fungible, + NonFungible, +} + +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +pub enum WildMultiAsset { + All, + // TODO: AllOf { fun: WildFungibility, id: AssetId } + AllOf(WildFungibility, AssetId), +} + +impl Encode for WildMultiAsset { + fn encode(&self) -> Vec { + OldMultiAsset::from(self.clone()).encode() + } +} + +impl Decode for WildMultiAsset { fn decode(input: &mut I) -> Result { OldMultiAsset::decode(input).map(Into::into) } } -impl From for OldMultiAsset { - fn from(a: MultiAsset) -> Self { - use {AssetId::*, Fungibility::*, OldMultiAsset::*, MultiAsset::Asset}; +impl From for OldMultiAsset { + fn from(a: WildMultiAsset) -> Self { + use {AssetId::*, Fungibility::*, OldMultiAsset::*, WildMultiAsset::AllOf}; match a { - MultiAsset::None => OldMultiAsset::None, - MultiAsset::All => All, + WildMultiAsset::All => All, + AllOf(WildFungibility::Fungible, Concrete(id)) => AllConcreteFungible { id }, + AllOf(WildFungibility::Fungible, Abstract(id)) => AllAbstractFungible { id }, + AllOf(WildFungibility::NonFungible, Concrete(class)) => AllConcreteNonFungible { class }, + AllOf(WildFungibility::NonFungible, Abstract(class)) => AllAbstractNonFungible { class }, + } + } +} - Asset(Fungible(_), Option::None) => AllFungible, - Asset(NonFungible(_), Option::None) => AllNonFungible, +impl TryFrom for WildMultiAsset { + type Error = (); + fn try_from(a: OldMultiAsset) -> Result { + use {AssetId::*, Fungibility::*, OldMultiAsset::*, WildMultiAsset::AllOf}; + Ok(match a { + All => WildMultiAsset::All, + AllConcreteFungible { id } => AllOf(WildFungibility::Fungible, Concrete(id)), + AllAbstractFungible { id } => AllOf(WildFungibility::Fungible, Abstract(id)), + AllConcreteNonFungible { class } => AllOf(WildFungibility::NonFungible, Concrete(class)), + AllAbstractNonFungible { class } => AllOf(WildFungibility::NonFungible, Abstract(class)), + _ => return Err(()), + }) + } +} - Asset(Fungible(Option::None), Some(Concrete(id))) => AllConcreteFungible { id }, - Asset(Fungible(Option::None), Some(Abstract(id))) => AllAbstractFungible { id }, - Asset(NonFungible(Option::None), Some(Concrete(class))) => AllConcreteNonFungible { class }, - Asset(NonFungible(Option::None), Some(Abstract(class))) => AllAbstractNonFungible { class }, +impl WildMultiAsset { + /// 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 { + use WildMultiAsset::*; + match self { + AllOf(fun, id) => inner.fun.is_kind(*fun) && inner.id == id, + All => true, + } + } - Asset(Fungible(Some(amount)), Some(Concrete(id))) => ConcreteFungible { id, amount }, - Asset(Fungible(Some(amount)), Some(Abstract(id))) => AbstractFungible { id, amount }, - Asset(NonFungible(Some(instance)), Some(Concrete(class))) => ConcreteNonFungible { class, instance }, - Asset(NonFungible(Some(instance)), Some(Abstract(class))) => AbstractNonFungible { class, instance }, + /// Prepend a `MultiLocation` to any concrete asset components, giving it a new root location. + pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { + use WildMultiAsset::*; + match self { + AllOf(_, ref mut id) => id.prepend_with(prepend.clone()).map_err(|_| ()), + _ => Ok(()), } } } -impl From for MultiAsset { - fn from(a: OldMultiAsset) -> Self { - use {AssetId::*, Fungibility::*, OldMultiAsset::*, MultiAsset::Asset}; + + + + +/// `MultiAsset` collection, either `MultiAssets` or a single wildcard. Note: vectors of wildcards +/// whose encoding is supported in XCM v0 are unsupported in this implementation and will result in a decode error. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +pub enum MultiAssetFilter { + Assets(MultiAssets), + Wild(WildMultiAsset), +} + +impl From for Vec { + fn from(a: MultiAssetFilter) -> Self { + use MultiAssetFilter::*; match a { - None => MultiAsset::None, - All => MultiAsset::All, + Assets(assets) => assets.0.into_iter().map(OldMultiAsset::from).collect(), + Wild(wild) => vec![wild.into()], + } + } +} - AllFungible => Asset(Fungible(Option::None), Option::None), - AllNonFungible => Asset(NonFungible(Option::None), Option::None), +impl TryFrom> for MultiAssetFilter { + type Error = (); + fn try_from(old_assets: Vec) -> Result { + use MultiAssetFilter::*; + if old_assets.is_empty() { + return Ok(Assets(MultiAssets::new())) + } + if let (1, Ok(wild)) = (old_assets.len(), old_assets[0].try_into()) { + return Ok(Wild(wild)) + } - AllConcreteFungible { id } => Asset(Fungible(Option::None), Some(Concrete(id))), - AllAbstractFungible { id } => Asset(Fungible(Option::None), Some(Abstract(id))), - AllConcreteNonFungible { class } => Asset(NonFungible(Option::None), Some(Concrete(class))), - AllAbstractNonFungible { class } => Asset(NonFungible(Option::None), Some(Abstract(class))), + old_assets.into_iter().map(MultiAsset::try_from).collect() + } +} + +impl MultiAssetFilter { + /// Returns `true` if the `MultiAsset` is a wildcard and refers to sets of assets, instead of just one. + pub fn is_wildcard(&self) -> bool { + matches!(self, MultiAssetFilter::Wild(..)) + } - ConcreteFungible { id, amount } => Asset(Fungible(Some(amount)), Some(Concrete(id))), - AbstractFungible { id, amount } => Asset(Fungible(Some(amount)), Some(Abstract(id))), - ConcreteNonFungible { class, instance } => Asset(NonFungible(Some(instance)), Some(Concrete(class))), - AbstractNonFungible { class, instance } => Asset(NonFungible(Some(instance)), Some(Abstract(class))), + /// Returns `true` if the `MultiAsset` is not a wildcard. + pub fn is_definite(&self) -> bool { + !self.is_wildcard() + } + + /// Returns `true` if this definitely represents no asset. + pub fn is_none(&self) -> bool { + matches!(self, MultiAssetFilter::Assets(a) if a.is_none()) + } + + /// 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 { + match self { + MultiAssetFilter::Assets(ref assets) => assets.contains(inner), + MultiAssetFilter::Wild(ref wild) => wild.contains(inner), + } + } + + /// Prepend a `MultiLocation` to any concrete asset components, giving it a new root location. + pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { + match self { + MultiAssetFilter::Assets(ref mut assets) => assets.reanchor(prepend), + MultiAssetFilter::Wild(ref mut wild) => wild.reanchor(prepend), } } } diff --git a/xcm/src/v0/order.rs b/xcm/src/v0/order.rs index 776ac3691c74..fa8aa6df3393 100644 --- a/xcm/src/v0/order.rs +++ b/xcm/src/v0/order.rs @@ -19,7 +19,7 @@ use alloc::vec::Vec; use derivative::Derivative; use parity_scale_codec::{self, Encode, Decode}; -use super::{MultiAsset, MultiLocation, Xcm}; +use super::{MultiAsset, WildMultiAsset, MultiAssetFilter, MultiLocation, Xcm}; /// An instruction to be executed on some or all of the assets in holding, used by asset-related XCM messages. #[derive(Derivative, Encode, Decode)] @@ -39,7 +39,7 @@ pub enum Order { /// /// Errors: #[codec(index = 1)] - DepositAsset { assets: Vec, dest: MultiLocation }, + DepositAsset { assets: MultiAssetFilter, dest: MultiLocation }, /// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within /// this consensus system. @@ -53,19 +53,18 @@ pub enum Order { /// /// Errors: #[codec(index = 2)] - DepositReserveAsset { assets: Vec, dest: MultiLocation, effects: Vec> }, + DepositReserveAsset { assets: MultiAssetFilter, dest: MultiLocation, effects: Vec> }, /// Remove the asset(s) (`give`) from holding and replace them with alternative assets. /// /// The minimum amount of assets to be received into holding for the order not to fail may be stated. /// /// - `give`: The asset(s) to remove from holding. - /// - `receive`: The minimum amount of assets(s) which `give` should be exchanged for. The meaning of wildcards - /// is undefined and they should be not be used. + /// - `receive`: The minimum amount of assets(s) which `give` should be exchanged for. /// /// Errors: #[codec(index = 3)] - ExchangeAsset { give: Vec, receive: Vec }, + ExchangeAsset { give: MultiAssetFilter, receive: Vec }, /// Remove the asset(s) (`assets`) from holding and send a `WithdrawAsset` XCM message to a reserve location. /// @@ -77,7 +76,7 @@ pub enum Order { /// /// Errors: #[codec(index = 4)] - InitiateReserveWithdraw { assets: Vec, reserve: MultiLocation, effects: Vec> }, + InitiateReserveWithdraw { assets: MultiAssetFilter, reserve: MultiLocation, effects: Vec> }, /// Remove the asset(s) (`assets`) from holding and send a `TeleportAsset` XCM message to a destination location. /// @@ -87,7 +86,7 @@ pub enum Order { /// /// Errors: #[codec(index = 5)] - InitiateTeleport { assets: Vec, dest: MultiLocation, effects: Vec> }, + InitiateTeleport { assets: MultiAssetFilter, dest: MultiLocation, effects: Vec> }, /// Send a `Balances` XCM message with the `assets` value equal to the holding contents, or a portion thereof. /// @@ -99,14 +98,16 @@ pub enum Order { /// /// Errors: #[codec(index = 6)] - QueryHolding { #[codec(compact)] query_id: u64, dest: MultiLocation, assets: Vec }, + QueryHolding { #[codec(compact)] query_id: u64, dest: MultiLocation, assets: MultiAssetFilter }, /// Pay for the execution of some XCM with up to `weight` picoseconds of execution time, paying for this with /// up to `fees` from the holding account. /// + /// - `fees`: The asset(s) to remove from holding to pay for fees. + /// /// Errors: #[codec(index = 7)] - BuyExecution { fees: MultiAsset, weight: u64, debt: u64, halt_on_error: bool, xcm: Vec> }, + BuyExecution { fees: WildMultiAsset, weight: u64, debt: u64, halt_on_error: bool, xcm: Vec> }, } pub mod opaque { diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index 56d7d753e49e..6e9b7ff5a322 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -122,7 +122,7 @@ pub fn add_asset(who: u64, what: MultiAsset) { ASSETS.with(|a| a.borrow_mut() .entry(who) .or_insert(Assets::new()) - .saturating_subsume(what) + .subsume(what) ); } diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index c0d35052482b..97c5fbbc15a5 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -15,44 +15,16 @@ // along with Polkadot. If not, see . use sp_std::{prelude::*, mem, collections::{btree_map::BTreeMap, btree_set::BTreeSet}}; -use xcm::v0::{MultiAsset, MultiLocation, AssetInstance}; +use xcm::v0::{ + MultiAsset, MultiAssets, MultiLocation, AssetInstance, + MultiAssetFilter::{self, Assets, Wild}, + AssetId::{self, Concrete, Abstract}, + WildMultiAsset::{self, All, AllOf}, + Fungibility::{Fungible, NonFungible}, + WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible}, +}; use sp_runtime::RuntimeDebug; -/// Classification of an asset being concrete or abstract. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug)] -pub enum AssetId { - Concrete(MultiLocation), - Abstract(Vec), -} - -impl AssetId { - /// Prepend a `MultiLocation` to a concrete asset, giving it a new root location. - pub fn prepend_location(&mut self, prepend: &MultiLocation) -> Result<(), ()> { - if let AssetId::Concrete(ref mut l) = self { - l.prepend_with(prepend.clone()).map_err(|_| ())?; - } - Ok(()) - } - - /// Use the value of `self` along with an `amount to create the corresponding `MultiAsset` value for a - /// fungible asset. - pub fn into_fungible_multiasset(self, amount: u128) -> MultiAsset { - match self { - AssetId::Concrete(id) => MultiAsset::ConcreteFungible { id, amount }, - AssetId::Abstract(id) => MultiAsset::AbstractFungible { id, amount }, - } - } - - /// Use the value of `self` along with an `instance to create the corresponding `MultiAsset` value for a - /// non-fungible asset. - pub fn into_non_fungible_multiasset(self, instance: AssetInstance) -> MultiAsset { - match self { - AssetId::Concrete(class) => MultiAsset::ConcreteNonFungible { class, instance }, - AssetId::Abstract(class) => MultiAsset::AbstractNonFungible { class, instance }, - } - } -} - /// List of non-wildcard fungible and non-fungible assets. #[derive(Default, Clone, RuntimeDebug, Eq, PartialEq)] pub struct Assets { @@ -69,7 +41,7 @@ impl From> for Assets { fn from(assets: Vec) -> Assets { let mut result = Self::default(); for asset in assets.into_iter() { - result.saturating_subsume(asset) + result.subsume(asset) } result } @@ -84,181 +56,64 @@ impl From for Vec { impl From for Assets { fn from(asset: MultiAsset) -> Assets { let mut result = Self::default(); - result.saturating_subsume(asset); + result.subsume(asset); result } } +/// An error emitted by `take` operations. +pub enum TakeError { + /// There was an attempt to take an asset without saturating (enough of) which did not exist. + AssetUnderflow(MultiAsset), +} + impl Assets { /// New value, containing no assets. pub fn new() -> Self { Self::default() } - /// An iterator over the fungible assets. + /// A borrowing iterator over the fungible assets. pub fn fungible_assets_iter<'a>(&'a self) -> impl Iterator + 'a { - self.fungible.iter() - .map(|(id, &amount)| match id.clone() { - AssetId::Concrete(id) => MultiAsset::ConcreteFungible { id, amount }, - AssetId::Abstract(id) => MultiAsset::AbstractFungible { id, amount }, - }) + self.fungible.iter().map(|(id, &amount)| MultiAsset { fun: Fungible(amount), id: id.clone() }) } - /// An iterator over the non-fungible assets. + /// A borrowing iterator over the non-fungible assets. pub fn non_fungible_assets_iter<'a>(&'a self) -> impl Iterator + 'a { - self.non_fungible.iter() - .map(|&(ref class, ref instance)| match class.clone() { - AssetId::Concrete(class) => MultiAsset::ConcreteNonFungible { class, instance: instance.clone() }, - AssetId::Abstract(class) => MultiAsset::AbstractNonFungible { class, instance: instance.clone() }, - }) + self.non_fungible.iter().map(|(id, instance)| MultiAsset { fun: NonFungible(instance.clone()), id: id.clone() }) } - /// An iterator over all assets. + /// A consuming iterator over all assets. pub fn into_assets_iter(self) -> impl Iterator { - let fungible = self.fungible.into_iter() - .map(|(id, amount)| match id { - AssetId::Concrete(id) => MultiAsset::ConcreteFungible { id, amount }, - AssetId::Abstract(id) => MultiAsset::AbstractFungible { id, amount }, - }); - let non_fungible = self.non_fungible.into_iter() - .map(|(id, instance)| match id { - AssetId::Concrete(class) => MultiAsset::ConcreteNonFungible { class, instance }, - AssetId::Abstract(class) => MultiAsset::AbstractNonFungible { class, instance }, - }); - fungible.chain(non_fungible) - } - - /// An iterator over all assets. + self.fungible.into_iter().map(|(id, amount)| MultiAsset { fun: Fungible(amount), id }) + .chain(self.non_fungible.into_iter().map(|(id, instance)| MultiAsset { fun: NonFungible(instance), id })) + } + + /// A borrowing iterator over all assets. pub fn assets_iter<'a>(&'a self) -> impl Iterator + 'a { - let fungible = self.fungible_assets_iter(); - let non_fungible = self.non_fungible_assets_iter(); - fungible.chain(non_fungible) + self.fungible_assets_iter().chain(self.non_fungible_assets_iter()) } /// Mutate `self` to contain all given `assets`, saturating if necessary. - /// - /// Wildcards in `assets` are ignored. - pub fn saturating_subsume_all(&mut self, assets: Assets) { - // OPTIMIZE: Could be done with a much faster btree entry merge and only sum the entries with the - // same key. + pub fn subsume_assets(&mut self, assets: Assets) { + // TODO: Could be done with a much faster btree entry merge and only sum the entries with the + // same key. for asset in assets.into_assets_iter() { - self.saturating_subsume(asset) + self.subsume(asset) } } /// Mutate `self` to contain the given `asset`, saturating if necessary. /// /// Wildcard values of `asset` do nothing. - pub fn saturating_subsume(&mut self, asset: MultiAsset) { - match asset { - MultiAsset::ConcreteFungible { id, amount } => { - self.saturating_subsume_fungible(AssetId::Concrete(id), amount); - } - MultiAsset::AbstractFungible { id, amount } => { - self.saturating_subsume_fungible(AssetId::Abstract(id), amount); - } - MultiAsset::ConcreteNonFungible { class, instance} => { - self.saturating_subsume_non_fungible(AssetId::Concrete(class), instance); - } - MultiAsset::AbstractNonFungible { class, instance} => { - self.saturating_subsume_non_fungible(AssetId::Abstract(class), instance); - } - _ => (), + pub fn subsume(&mut self, asset: MultiAsset) { + match asset.fun { + Fungible(amount) => self.fungible + .entry(asset.id) + .and_modify(|e| *e = e.saturating_add(amount)) + .or_insert(amount), + NonFungible(instance) => self.non_fungible.insert((asset.id, instance)), } } - - /// Consumes `self` and returns its original value excluding `asset` iff it contains at least `asset`. - /// - /// Wildcard assets in `self` will result in an error. - /// - /// `asset` may be a wildcard and are evaluated in the context of `self`. - /// - /// Returns `Ok` with the `self` minus `asset` and the non-wildcard equivalence of `asset` taken if `self` - /// contains `asset`, and `Err` with `self` otherwise. - pub fn less(mut self, asset: MultiAsset) -> Result<(Self, Assets), Self> { - match self.try_take(asset) { - Ok(taken) => Ok((self, taken)), - Err(()) => Err(self), - } - } - - /// Mutates `self` to its original value less `asset` and returns `true` iff it contains at least `asset`. - /// - /// Wildcard assets in `self` will result in an error. - /// - /// `asset` may be a wildcard and are evaluated in the context of `self`. - /// - /// Returns `Ok` with the non-wildcard equivalence of `asset` taken and mutates `self` to its value minus - /// `asset` if `self` contains `asset`, and return `Err` otherwise. - pub fn try_take(&mut self, asset: MultiAsset) -> Result { - match asset { - MultiAsset::None => Ok(Assets::new()), - MultiAsset::ConcreteFungible { id, amount } => self.try_take_fungible(AssetId::Concrete(id), amount), - MultiAsset::AbstractFungible { id, amount } => self.try_take_fungible(AssetId::Abstract(id), amount), - MultiAsset::ConcreteNonFungible { class, instance} => self.try_take_non_fungible(AssetId::Concrete(class), instance), - MultiAsset::AbstractNonFungible { class, instance} => self.try_take_non_fungible(AssetId::Abstract(class), instance), - MultiAsset::AllAbstractFungible { id } => Ok(self.take_fungible(&AssetId::Abstract(id))), - MultiAsset::AllConcreteFungible { id } => Ok(self.take_fungible(&AssetId::Concrete(id))), - MultiAsset::AllAbstractNonFungible { class } => Ok(self.take_non_fungible(&AssetId::Abstract(class))), - MultiAsset::AllConcreteNonFungible { class } => Ok(self.take_non_fungible(&AssetId::Concrete(class))), - MultiAsset::AllFungible => { - let mut taken = Assets::new(); - mem::swap(&mut self.fungible, &mut taken.fungible); - Ok(taken) - }, - MultiAsset::AllNonFungible => { - let mut taken = Assets::new(); - mem::swap(&mut self.non_fungible, &mut taken.non_fungible); - Ok(taken) - }, - MultiAsset::All => Ok(self.swapped(Assets::new())), - } - } - - pub fn try_take_fungible(&mut self, id: AssetId, amount: u128) -> Result { - self.try_remove_fungible(&id, amount)?; - Ok(id.into_fungible_multiasset(amount).into()) - } - - pub fn try_take_non_fungible(&mut self, id: AssetId, instance: AssetInstance) -> Result { - let asset_id_instance = (id, instance); - self.try_remove_non_fungible(&asset_id_instance)?; - let (asset_id, instance) = asset_id_instance; - Ok(asset_id.into_non_fungible_multiasset(instance).into()) - } - - pub fn take_fungible(&mut self, id: &AssetId) -> Assets { - let mut taken = Assets::new(); - if let Some((id, amount)) = self.fungible.remove_entry(&id) { - taken.fungible.insert(id, amount); - } - taken - } - - pub fn take_non_fungible(&mut self, id: &AssetId) -> Assets { - let mut taken = Assets::new(); - let non_fungible = mem::replace(&mut self.non_fungible, Default::default()); - non_fungible.into_iter().for_each(|(c, instance)| { - if &c == id { - taken.non_fungible.insert((c, instance)); - } else { - self.non_fungible.insert((c, instance)); - } - }); - taken - } - - pub fn try_remove_fungible(&mut self, id: &AssetId, amount: u128) -> Result<(), ()> { - let self_amount = self.fungible.get_mut(&id).ok_or(())?; - *self_amount = self_amount.checked_sub(amount).ok_or(())?; - Ok(()) - } - - pub fn try_remove_non_fungible(&mut self, class_instance: &(AssetId, AssetInstance)) -> Result<(), ()> { - match self.non_fungible.remove(class_instance) { - true => Ok(()), - false => Err(()), - } - } - +/* /// Modify `self` to include a new fungible asset by `id` and `amount`, /// saturating if necessary. pub fn saturating_subsume_fungible(&mut self, id: AssetId, amount: u128) { @@ -272,6 +127,12 @@ impl Assets { pub fn saturating_subsume_non_fungible(&mut self, class: AssetId, instance: AssetInstance) { self.non_fungible.insert((class, instance)); } +*/ + /// Swaps two mutable Assets, without deinitializing either one. + pub fn swapped(&mut self, mut with: Assets) -> Self { + mem::swap(&mut *self, &mut with); + with + } /// Alter any concretely identified assets by prepending the given `MultiLocation`. /// @@ -290,6 +151,122 @@ impl Assets { .collect(); } + /// Returns an error unless all `assets` are contained in `self`. In the case of an error, the first asset in + /// `assets` which is not wholly in `self` is returned. + fn ensure_contains(&self, assets: &MultiAssets) -> Result<(), TakeError> { + for asset in assets.inner().iter() { + match asset { + MultiAsset { fun: Fungible(ref amount), ref id } => { + if self.fungible.get(id).map_or(true, |a| a < amount) { + return Err(TakeError((id.clone(), amount).into())) + } + } + MultiAsset { fun: NonFungible(ref instance), ref id } => { + let id_instance = (id.clone(), instance.clone()); + if !self.non_fungible.contains(&id_instance) { + return Err(TakeError(id_instance.into())) + } + } + } + } + return Ok(()) + } + + /// Mutates `self` to its original value less `mask` and returns `true`. + /// + /// If `saturate` is `true`, then `self` is considered to be masked by `mask`, thereby avoiding any attempt at + /// reducing it by assets it does not contain. In this case, the function is infallible. If `saturate` is `false` + /// and `asset` references a definite asset which `self` does not contain then an error is returned. + /// + /// Returns `Ok` with the definite assets token from `self` and mutates `self` to its value minus + /// `asset`. Returns `Err` in the non-saturating case where `self` did not contain (enough of) a definite asset to + /// be removed. + fn general_take(&mut self, mask: MultiAssetFilter, saturate: bool) -> Result { + match mask { + Wild(All) => Ok(self.swapped(Assets::new())), + Wild(AllOf(WildFungible, id)) => { + let mut taken = Assets::new(); + if let Some((id, amount)) = self.fungible.remove_entry(&id) { + taken.fungible.insert(id, amount); + } + Ok(taken) + } + Wild(AllOf(WildNonFungible, id)) => { + let mut taken = Assets::new(); + let non_fungible = mem::replace(&mut self.non_fungible, Default::default()); + non_fungible.into_iter().for_each(|(c, instance)| { + if &c == id { + taken.non_fungible.insert((c, instance)); + } else { + self.non_fungible.insert((c, instance)); + } + }); + Ok(taken) + } + Assets(assets) => { + if !saturate { + self.ensure_contains(&assets)?; + } + let mut taken = Assets::new(); + for asset in assets.drain().into_iter() { + match asset { + MultiAsset { fun: Fungible(mut amount), id } => { + let (remove, amount) = match self.fungible.get_mut(&id) { + Some(self_amount) => { + let amount = amount.min(*self_amount); + *self_amount -= amount; + (self_amount == 0, amount) + } + None => (false, 0), + }; + if remove { + self.fungible.remove(&id); + } + if amount > 0 { + taken.subsume(MultiAsset::from((id, amount)).into()); + } + } + MultiAsset { fun: NonFungible(instance), id } => { + let id_instance = (id, instance); + if self.non_fungible.remove(&id_instance) { + taken.subsume(id_instance.into()) + } + } + } + } + } + } + } + + /// Mutates `self` to its original value less `asset` and returns `true` iff it contains at least `asset`. + /// + /// `asset` is interpreted as being masked under `self`. + /// + /// Returns `Ok` with the non-wildcard equivalence of `asset` taken and mutates `self` to its value minus + /// `asset` if `self` contains `asset`, and return `Err` otherwise. + pub fn saturating_take(&mut self, asset: MultiAssetFilter) -> Assets { + self.general_take(asset, true).expect("general_take never results in error when saturating") + } + + /// Mutates `self` to its original value less `asset` and returns `true` iff it contains at least `asset`. + /// + /// `asset` is interpreted as being masked under `self`. + /// + /// Returns `Ok` with the non-wildcard equivalence of `asset` taken and mutates `self` to its value minus + /// `asset` if `self` contains `asset`, and return `Err` otherwise. + pub fn try_take(&mut self, asset: MultiAssetFilter) -> Result { + self.general_take(asset, false) + } + + /// Consumes `self` and returns its original value excluding `asset` iff it contains at least `asset`, as well as + /// the assets excluded. + pub fn less(mut self, asset: WildMultiAsset) -> Result<(Assets, Assets), Self> { + match self.try_take(asset) { + Ok(taken) => Ok((self, taken)), + Err(_) => Err(self), + } + } + /// Return the assets in `self`, but (asset-wise) of no greater value than `assets`. /// /// Result is undefined if `assets` includes elements which match to the same asset more than once. @@ -314,223 +291,41 @@ impl Assets { /// MultiAsset::AbstractFungible { id: vec![0], amount: 50 }, /// ]); /// ``` - pub fn min<'a, M, I>(&self, assets: I) -> Self - where - M: 'a + sp_std::borrow::Borrow, - I: IntoIterator, - { - let mut result = Assets::default(); - for asset in assets.into_iter() { - match asset.borrow() { - MultiAsset::None => (), - MultiAsset::All => return self.clone(), - MultiAsset::AllFungible => { - // Replace `result.fungible` with all fungible assets, - // keeping `result.non_fungible` the same. - result = Assets { - fungible: self.fungible.clone(), - non_fungible: result.non_fungible, - } - }, - MultiAsset::AllNonFungible => { - // Replace `result.non_fungible` with all non-fungible assets, - // keeping `result.fungible` the same. - result = Assets { - fungible: result.fungible, - non_fungible: self.non_fungible.clone(), - } - }, - MultiAsset::AllAbstractFungible { id } => { - for asset in self.fungible_assets_iter() { - match &asset { - MultiAsset::AbstractFungible { id: identifier, .. } => { - if id == identifier { result.saturating_subsume(asset) } - }, - _ => (), - } - } - }, - MultiAsset::AllAbstractNonFungible { class } => { - for asset in self.non_fungible_assets_iter() { - match &asset { - MultiAsset::AbstractNonFungible { class: c, .. } => { - if class == c { result.saturating_subsume(asset) } - }, - _ => (), - } - } + pub fn min(&self, mask: MultiAssetFilter) -> Assets { + let mut masked = Assets::new(); + match mask { + Wild(All) => Ok(self.clone()), + Wild(AllOf(WildFungible, id)) => { + if let Some(&(ref id, amount)) = self.fungible.get(&id) { + masked.fungible.insert(id.clone(), amount); } - MultiAsset::AllConcreteFungible { id } => { - for asset in self.fungible_assets_iter() { - match &asset { - MultiAsset::ConcreteFungible { id: identifier, .. } => { - if id == identifier { result.saturating_subsume(asset) } - }, - _ => (), - } - } - }, - MultiAsset::AllConcreteNonFungible { class } => { - for asset in self.non_fungible_assets_iter() { - match &asset { - MultiAsset::ConcreteNonFungible { class: c, .. } => { - if class == c { result.saturating_subsume(asset) } - }, - _ => (), - } - } - } - x @ MultiAsset::ConcreteFungible { .. } | x @ MultiAsset::AbstractFungible { .. } => { - let (id, amount) = match x { - MultiAsset::ConcreteFungible { id, amount } => (AssetId::Concrete(id.clone()), *amount), - MultiAsset::AbstractFungible { id, amount } => (AssetId::Abstract(id.clone()), *amount), - _ => unreachable!(), - }; - if let Some(v) = self.fungible.get(&id) { - result.saturating_subsume_fungible(id, amount.min(*v)); - } - }, - x @ MultiAsset::ConcreteNonFungible { .. } | x @ MultiAsset::AbstractNonFungible { .. } => { - let (class, instance) = match x { - MultiAsset::ConcreteNonFungible { class, instance } => (AssetId::Concrete(class.clone()), instance.clone()), - MultiAsset::AbstractNonFungible { class, instance } => (AssetId::Abstract(class.clone()), instance.clone()), - _ => unreachable!(), - }; - let item = (class, instance); - if self.non_fungible.contains(&item) { - result.non_fungible.insert(item); + } + Wild(AllOf(WildNonFungible, id)) => { + self.non_fungible.iter().for_each(|(ref c, ref instance)| { + if c == id { + masked.non_fungible.insert((c.clone(), instance.clone())); } - } + }); } - } - result - } - - /// Take all possible assets up to `assets` from `self`, mutating `self` and returning the - /// assets taken. - /// - /// Wildcards work. - /// - /// Example: - /// - /// ``` - /// use xcm_executor::Assets; - /// use xcm::v0::{MultiAsset, MultiLocation}; - /// let mut assets_i_have: Assets = vec![ - /// MultiAsset::ConcreteFungible { id: MultiLocation::Null, amount: 100 }, - /// MultiAsset::AbstractFungible { id: vec![0], amount: 100 }, - /// ].into(); - /// let assets_they_want = vec![ - /// MultiAsset::AllAbstractFungible { id: vec![0] }, - /// ]; - /// - /// let assets_they_took: Assets = assets_i_have.saturating_take(assets_they_want); - /// assert_eq!(assets_they_took.into_assets_iter().collect::>(), vec![ - /// MultiAsset::AbstractFungible { id: vec![0], amount: 100 }, - /// ]); - /// assert_eq!(assets_i_have.into_assets_iter().collect::>(), vec![ - /// MultiAsset::ConcreteFungible { id: MultiLocation::Null, amount: 100 }, - /// ]); - /// ``` - pub fn saturating_take(&mut self, assets: I) -> Assets - where - I: IntoIterator, - { - let mut result = Assets::default(); - for asset in assets.into_iter() { - match asset { - MultiAsset::None => (), - MultiAsset::All => return self.swapped(Assets::default()), - MultiAsset::AllFungible => { - // Remove all fungible assets, and copy them into `result`. - let fungible = mem::replace(&mut self.fungible, Default::default()); - fungible.into_iter().for_each(|(id, amount)| { - result.saturating_subsume_fungible(id, amount); - }) - }, - MultiAsset::AllNonFungible => { - // Remove all non-fungible assets, and copy them into `result`. - let non_fungible = mem::replace(&mut self.non_fungible, Default::default()); - non_fungible.into_iter().for_each(|(class, instance)| { - result.saturating_subsume_non_fungible(class, instance); - }); - }, - x @ MultiAsset::AllAbstractFungible { .. } | x @ MultiAsset::AllConcreteFungible { .. } => { - let id = match x { - MultiAsset::AllConcreteFungible { id } => AssetId::Concrete(id), - MultiAsset::AllAbstractFungible { id } => AssetId::Abstract(id), - _ => unreachable!(), - }; - // At the end of this block, we will be left with only the non-matching fungibles. - let mut non_matching_fungibles = BTreeMap::::new(); - let fungible = mem::replace(&mut self.fungible, Default::default()); - fungible.into_iter().for_each(|(iden, amount)| { - if iden == id { - result.saturating_subsume_fungible(iden, amount); - } else { - non_matching_fungibles.insert(iden, amount); + Assets(assets) => { + for asset in assets.inner().iter() { + match asset { + MultiAsset { fun: Fungible(ref amount), ref id } => { + if let Some(m) = self.fungible.get(id) { + masked.subsume((id.clone(), Fungible(*amount.min(m))).into()); } - }); - self.fungible = non_matching_fungibles; - }, - x @ MultiAsset::AllAbstractNonFungible { .. } | x @ MultiAsset::AllConcreteNonFungible { .. } => { - let class = match x { - MultiAsset::AllConcreteNonFungible { class } => AssetId::Concrete(class), - MultiAsset::AllAbstractNonFungible { class } => AssetId::Abstract(class), - _ => unreachable!(), - }; - // At the end of this block, we will be left with only the non-matching non-fungibles. - let mut non_matching_non_fungibles = BTreeSet::<(AssetId, AssetInstance)>::new(); - let non_fungible = mem::replace(&mut self.non_fungible, Default::default()); - non_fungible.into_iter().for_each(|(c, instance)| { - if class == c { - result.saturating_subsume_non_fungible(c, instance); - } else { - non_matching_non_fungibles.insert((c, instance)); + } + MultiAsset { fun: NonFungible(ref instance), ref id } => { + let id_instance = (id.clone(), instance.clone()); + if self.non_fungible.contains(&id_instance) { + masked.subsume(id_instance.into()); } - }); - self.non_fungible = non_matching_non_fungibles; - }, - x @ MultiAsset::ConcreteFungible {..} | x @ MultiAsset::AbstractFungible {..} => { - let (id, amount) = match x { - MultiAsset::ConcreteFungible { id, amount } => (AssetId::Concrete(id), amount), - MultiAsset::AbstractFungible { id, amount } => (AssetId::Abstract(id), amount), - _ => unreachable!(), - }; - // remove the maxmimum possible up to id/amount from self, add the removed onto - // result - let maybe_value = self.fungible.get(&id); - if let Some(&e) = maybe_value { - if e > amount { - self.fungible.insert(id.clone(), e - amount); - result.saturating_subsume_fungible(id, amount); - } else { - self.fungible.remove(&id); - result.saturating_subsume_fungible(id, e.clone()); } } } - x @ MultiAsset::ConcreteNonFungible {..} | x @ MultiAsset::AbstractNonFungible {..} => { - let (class, instance) = match x { - MultiAsset::ConcreteNonFungible { class, instance } => (AssetId::Concrete(class), instance), - MultiAsset::AbstractNonFungible { class, instance } => (AssetId::Abstract(class), instance), - _ => unreachable!(), - }; - // remove the maxmimum possible up to id/amount from self, add the removed onto - // result - if let Some(entry) = self.non_fungible.take(&(class, instance)) { - result.non_fungible.insert(entry); - } - } } } - result - } - - /// Swaps two mutable Assets, without deinitializing either one. - pub fn swapped(&mut self, mut with: Assets) -> Self { - mem::swap(&mut *self, &mut with); - with + masked } } diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 8f8a5c9ee617..87b770533db2 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -106,10 +106,10 @@ impl XcmExecutor { log::trace!( target: "xcm::do_execute_xcm", "origin: {:?}, top_level: {:?}, message: {:?}, weight_credit: {:?}, maybe_shallow_weight: {:?}", - origin, - top_level, - message, - weight_credit, + origin, + top_level, + message, + weight_credit, maybe_shallow_weight, ); // This is the weight of everything that cannot be paid for. This basically means all computation @@ -133,7 +133,7 @@ impl XcmExecutor { for asset in assets { ensure!(!asset.is_wildcard(), XcmError::Wildcard); let withdrawn = Config::AssetTransactor::withdraw_asset(&asset, &origin)?; - holding.saturating_subsume_all(withdrawn); + holding.subsume_assets(withdrawn); } Some((holding, effects)) } @@ -288,7 +288,7 @@ impl XcmExecutor { let purchasing_weight = Weight::from(weight.checked_add(debt).ok_or(XcmError::Overflow)?); let max_fee = holding.try_take(fees).map_err(|()| XcmError::NotHoldingFees)?; let unspent = trader.buy_weight(purchasing_weight, max_fee)?; - holding.saturating_subsume_all(unspent); + holding.subsume_assets(unspent); let mut remaining_weight = weight; for message in xcm.into_iter() { @@ -298,7 +298,7 @@ impl XcmExecutor { Ok(surplus) => { total_surplus += surplus } } } - holding.saturating_subsume(trader.refund_weight(remaining_weight)); + holding.subsume(trader.refund_weight(remaining_weight)); } _ => return Err(XcmError::UnhandledEffect)?, } From 2661b91a5cc1ea6721e067db71cd1e9bb000fcb3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 30 Jul 2021 19:59:10 +0200 Subject: [PATCH 03/84] XCM core builds --- xcm/src/v0/mod.rs | 11 ++++- xcm/src/v0/multi_asset.rs | 3 +- xcm/src/v0/multiasset.rs | 79 ++++++++++++++++++---------------- xcm/xcm-executor/src/assets.rs | 14 ------ 4 files changed, 51 insertions(+), 56 deletions(-) diff --git a/xcm/src/v0/mod.rs b/xcm/src/v0/mod.rs index cc8044ea19d2..8f16bee29b39 100644 --- a/xcm/src/v0/mod.rs +++ b/xcm/src/v0/mod.rs @@ -20,7 +20,7 @@ use core::{result, convert::TryFrom, fmt::Debug}; use derivative::Derivative; use alloc::vec::Vec; use parity_scale_codec::{self, Encode, Decode}; -use crate::{VersionedMultiAsset, VersionedWildMultiAsset, DoubleEncoded, VersionedXcm}; +use crate::{DoubleEncoded, VersionedXcm}; mod junction; mod multi_asset; @@ -40,7 +40,14 @@ pub use traits::{Error, Result, SendXcm, ExecuteXcm, Outcome}; /// A prelude for importing all types typically used when interacting with XCM messages. pub mod prelude { pub use super::junction::{Junction::*, NetworkId, BodyId, BodyPart}; - pub use super::multi_asset::{MultiAsset::{self, *}, AssetInstance::{self, *}}; + pub use super::multiasset::{ + AssetId, MultiAssets, MultiAsset, + AssetInstance::{self, *}, + MultiAssetFilter::{self, *}, + Fungibility::{self, *}, + WildMultiAsset::{self, *}, + WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, + }; pub use super::multi_location::MultiLocation::{self, *}; pub use super::order::Order::{self, *}; pub use super::traits::{Error as XcmError, Result as XcmResult, SendXcm, ExecuteXcm, Outcome}; diff --git a/xcm/src/v0/multi_asset.rs b/xcm/src/v0/multi_asset.rs index d7065d3e1d8e..fe53f6905aa5 100644 --- a/xcm/src/v0/multi_asset.rs +++ b/xcm/src/v0/multi_asset.rs @@ -16,11 +16,10 @@ //! Cross-Consensus Message format data structures. -use core::{result, convert::TryFrom}; use alloc::vec::Vec; use parity_scale_codec::{self, Encode, Decode}; -use super::{MultiLocation, VersionedMultiAsset, AssetInstance}; +use super::{MultiLocation, AssetInstance}; /// A single general identifier for an asset. /// diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index 010f1ce4c1da..45e19323e04a 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -24,11 +24,10 @@ //! account. use core::convert::{TryFrom, TryInto}; -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; use parity_scale_codec::{self as codec, Encode, Decode}; use super::{ - MultiLocation, multi_asset::MultiAsset as OldMultiAsset, VersionedMultiAsset, - VersionedWildMultiAsset, + MultiLocation, multi_asset::MultiAsset as OldMultiAsset, }; use core::cmp::Ordering; @@ -79,15 +78,9 @@ impl AssetId { MultiAsset { fun, id: self } } - /// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `WildMultiAsset` - /// definite (`Asset`) value. - pub fn into_wild(self, fun: Fungibility) -> WildMultiAsset { - WildMultiAsset::Asset(fun, self) - } - /// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `WildMultiAsset` /// wildcard (`AllOf`) value. - pub fn into_allof(self, fun: WildFungibility) -> WildMultiAsset { + pub fn into_wild(self, fun: WildFungibility) -> WildMultiAsset { WildMultiAsset::AllOf(fun, self) } } @@ -137,10 +130,10 @@ impl PartialOrd for MultiAsset { impl Ord for MultiAsset { fn cmp(&self, other: &Self) -> Ordering { match (&self.fun, &other.fun) { - (Fungibility::Fungible(..), Fungibility::NonFungible(..)) => return Ordering::Less, - (Fungibility::NonFungible(..), Fungibility::Fungible(..)) => return Ordering::Greater, + (Fungibility::Fungible(..), Fungibility::NonFungible(..)) => Ordering::Less, + (Fungibility::NonFungible(..), Fungibility::Fungible(..)) => Ordering::Greater, + _ => (&self.id, &self.fun).cmp(&(&other.id, &other.fun)), } - (&self.id, &self.fun).cmp((&other.id, &other.fun)) } } @@ -197,18 +190,17 @@ impl Encode for MultiAsset { impl Decode for MultiAsset { fn decode(input: &mut I) -> Result { - OldMultiAsset::decode(input) - .and_then(|r| TryInto::try_into(r).map_err(|_| "Unsupported wildcard".into()))? + OldMultiAsset::decode(input)?.try_into().map_err(|_| "Unsupported wildcard".into()) } } impl MultiAsset { - fn is_fungible(&self, maybe_id: Option) -> bool { + pub fn is_fungible(&self, maybe_id: Option) -> bool { use Fungibility::*; matches!(self.fun, Fungible(..)) && maybe_id.map_or(true, |i| i == self.id) } - fn is_non_fungible(&self, maybe_id: Option) -> bool { + pub fn is_non_fungible(&self, maybe_id: Option) -> bool { use Fungibility::*; matches!(self.fun, NonFungible(..)) && maybe_id.map_or(true, |i| i == self.id) } @@ -220,7 +212,7 @@ impl MultiAsset { /// Returns true if `self` is a super-set of the given `inner`. pub fn contains(&self, inner: &MultiAsset) -> bool { - use {MultiAsset::*, Fungibility::*}; + use Fungibility::*; if self.id == inner.id { match (&self.fun, &inner.fun) { (Fungible(a), Fungible(i)) if a >= i => return true, @@ -242,7 +234,7 @@ impl Decode for MultiAssets { fn decode(input: &mut I) -> Result { let r = Vec::::decode(input)?; if r.is_empty() { return Ok(Self(Vec::new())) } - r.iter().skip(1).try_fold(&r[0], |a, b| { + r.iter().skip(1).try_fold(&r[0], |a, b| -> Result<&MultiAsset, parity_scale_codec::Error> { if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) { Ok(b) } else { @@ -262,7 +254,7 @@ impl From for Vec { impl TryFrom> for MultiAssets { type Error = (); fn try_from(a: Vec) -> Result { - a.0.into_iter().map(MultiAsset::try_from).collect() + a.into_iter().map(MultiAsset::try_from).collect::>().map(Self) } } @@ -293,7 +285,6 @@ impl MultiAssets { /// Returns true if `self` is a super-set of the given `inner`. pub fn contains(&self, inner: &MultiAsset) -> bool { - use {MultiAsset::*, Fungibility::*}; self.0.iter().any(|i| i.contains(inner)) } @@ -339,19 +330,19 @@ impl Encode for WildMultiAsset { impl Decode for WildMultiAsset { fn decode(input: &mut I) -> Result { - OldMultiAsset::decode(input).map(Into::into) + OldMultiAsset::decode(input)?.try_into().map_err(|()| "Invalid wildcard item".into()) } } impl From for OldMultiAsset { fn from(a: WildMultiAsset) -> Self { - use {AssetId::*, Fungibility::*, OldMultiAsset::*, WildMultiAsset::AllOf}; + use {AssetId::*, WildFungibility::*, OldMultiAsset::*, WildMultiAsset::AllOf}; match a { WildMultiAsset::All => All, - AllOf(WildFungibility::Fungible, Concrete(id)) => AllConcreteFungible { id }, - AllOf(WildFungibility::Fungible, Abstract(id)) => AllAbstractFungible { id }, - AllOf(WildFungibility::NonFungible, Concrete(class)) => AllConcreteNonFungible { class }, - AllOf(WildFungibility::NonFungible, Abstract(class)) => AllAbstractNonFungible { class }, + AllOf(Fungible, Concrete(id)) => AllConcreteFungible { id }, + AllOf(Fungible, Abstract(id)) => AllAbstractFungible { id }, + AllOf(NonFungible, Concrete(class)) => AllConcreteNonFungible { class }, + AllOf(NonFungible, Abstract(class)) => AllAbstractNonFungible { class }, } } } @@ -359,13 +350,13 @@ impl From for OldMultiAsset { impl TryFrom for WildMultiAsset { type Error = (); fn try_from(a: OldMultiAsset) -> Result { - use {AssetId::*, Fungibility::*, OldMultiAsset::*, WildMultiAsset::AllOf}; + use {AssetId::*, WildFungibility::*, OldMultiAsset::*, WildMultiAsset::AllOf}; Ok(match a { All => WildMultiAsset::All, - AllConcreteFungible { id } => AllOf(WildFungibility::Fungible, Concrete(id)), - AllAbstractFungible { id } => AllOf(WildFungibility::Fungible, Abstract(id)), - AllConcreteNonFungible { class } => AllOf(WildFungibility::NonFungible, Concrete(class)), - AllAbstractNonFungible { class } => AllOf(WildFungibility::NonFungible, Abstract(class)), + AllConcreteFungible { id } => AllOf(Fungible, Concrete(id)), + AllAbstractFungible { id } => AllOf(Fungible, Abstract(id)), + AllConcreteNonFungible { class } => AllOf(NonFungible, Concrete(class)), + AllAbstractNonFungible { class } => AllOf(NonFungible, Abstract(class)), _ => return Err(()), }) } @@ -379,7 +370,7 @@ impl WildMultiAsset { pub fn contains(&self, inner: &MultiAsset) -> bool { use WildMultiAsset::*; match self { - AllOf(fun, id) => inner.fun.is_kind(*fun) && inner.id == id, + AllOf(fun, id) => inner.fun.is_kind(*fun) && &inner.id == id, All => true, } } @@ -388,7 +379,7 @@ impl WildMultiAsset { pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { use WildMultiAsset::*; match self { - AllOf(_, ref mut id) => id.prepend_with(prepend.clone()).map_err(|_| ()), + AllOf(_, ref mut id) => id.reanchor(prepend).map_err(|_| ()), _ => Ok(()), } } @@ -418,16 +409,28 @@ impl From for Vec { impl TryFrom> for MultiAssetFilter { type Error = (); - fn try_from(old_assets: Vec) -> Result { + fn try_from(mut old_assets: Vec) -> Result { use MultiAssetFilter::*; if old_assets.is_empty() { return Ok(Assets(MultiAssets::new())) } - if let (1, Ok(wild)) = (old_assets.len(), old_assets[0].try_into()) { - return Ok(Wild(wild)) + if old_assets.len() == 1 && old_assets[0].is_wildcard() { + return old_assets.pop().ok_or(()).and_then(|i| Ok(Wild(i.try_into()?))) } - old_assets.into_iter().map(MultiAsset::try_from).collect() + MultiAssets::try_from(old_assets).map(Self::Assets) + } +} + +impl Encode for MultiAssetFilter { + fn encode(&self) -> Vec { + Vec::::from(self.clone()).encode() + } +} + +impl Decode for MultiAssetFilter { + fn decode(input: &mut I) -> Result { + Vec::::decode(input)?.try_into().map_err(|()| "Invalid items".into()) } } diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 97c5fbbc15a5..59a6b9fd81e6 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -113,21 +113,7 @@ impl Assets { NonFungible(instance) => self.non_fungible.insert((asset.id, instance)), } } -/* - /// Modify `self` to include a new fungible asset by `id` and `amount`, - /// saturating if necessary. - pub fn saturating_subsume_fungible(&mut self, id: AssetId, amount: u128) { - self.fungible - .entry(id) - .and_modify(|e| *e = e.saturating_add(amount)) - .or_insert(amount); - } - /// Modify `self` to include a new non-fungible asset by `class` and `instance`. - pub fn saturating_subsume_non_fungible(&mut self, class: AssetId, instance: AssetInstance) { - self.non_fungible.insert((class, instance)); - } -*/ /// Swaps two mutable Assets, without deinitializing either one. pub fn swapped(&mut self, mut with: Assets) -> Self { mem::swap(&mut *self, &mut with); From 6fc030eac3753c6519fa0a6f67e5e1b580a2c547 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 30 Jul 2021 21:13:15 +0200 Subject: [PATCH 04/84] XCM Executor builds --- xcm/src/v0/mod.rs | 14 +-- xcm/src/v0/multiasset.rs | 12 ++ xcm/xcm-builder/src/weight.rs | 13 +-- xcm/xcm-executor/src/assets.rs | 105 ++++++++++-------- xcm/xcm-executor/src/lib.rs | 42 +++---- xcm/xcm-executor/src/traits/transact_asset.rs | 8 +- xcm/xcm-executor/src/traits/weight.rs | 2 +- 7 files changed, 103 insertions(+), 93 deletions(-) diff --git a/xcm/src/v0/mod.rs b/xcm/src/v0/mod.rs index 8f16bee29b39..2d4c192d3ec0 100644 --- a/xcm/src/v0/mod.rs +++ b/xcm/src/v0/mod.rs @@ -54,7 +54,7 @@ pub mod prelude { pub use super::{Xcm::{self, *}, OriginKind}; } -// TODO: #2841 #XCMENCODE Efficient encodings for Vec, Vec, using initial byte values 128+ to encode +// TODO: #2841 #XCMENCODE Efficient encodings for MultiAssets, Vec, using initial byte values 128+ to encode // the number of items in the vector. /// Basically just the XCM (more general) version of `ParachainDispatchOrigin`. @@ -84,7 +84,7 @@ pub enum OriginKind { #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)] pub enum Response { /// Some assets. - Assets(Vec), + Assets(MultiAssets), } /// Cross-Consensus Message: A message from one consensus system to another. @@ -110,7 +110,7 @@ pub enum Xcm { /// /// Errors: #[codec(index = 0)] - WithdrawAsset { assets: Vec, effects: Vec> }, + WithdrawAsset { assets: MultiAssets, effects: Vec> }, /// Asset(s) (`assets`) have been received into the ownership of this system on the `origin` system. /// @@ -127,7 +127,7 @@ pub enum Xcm { /// /// Errors: #[codec(index = 1)] - ReserveAssetDeposit { assets: Vec, effects: Vec> }, + ReserveAssetDeposit { assets: MultiAssets, effects: Vec> }, /// Asset(s) (`assets`) have been destroyed on the `origin` system and equivalent assets should be /// created on this system. @@ -145,7 +145,7 @@ pub enum Xcm { /// /// Errors: #[codec(index = 2)] - TeleportAsset { assets: Vec, effects: Vec> }, + TeleportAsset { assets: MultiAssets, effects: Vec> }, /// Indication of the contents of the holding account corresponding to the `QueryHolding` order of `query_id`. /// @@ -172,7 +172,7 @@ pub enum Xcm { /// /// Errors: #[codec(index = 4)] - TransferAsset { assets: Vec, dest: MultiLocation }, + TransferAsset { assets: MultiAssets, dest: MultiLocation }, /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the /// ownership of `dest` within this consensus system. @@ -190,7 +190,7 @@ pub enum Xcm { /// /// Errors: #[codec(index = 5)] - TransferReserveAsset { assets: Vec, dest: MultiLocation, effects: Vec> }, + TransferReserveAsset { assets: MultiAssets, dest: MultiLocation, effects: Vec> }, /// Apply the encoded transaction `call`, whose dispatch-origin should be `origin` as expressed by the kind /// of origin `origin_type`. diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index 45e19323e04a..3d0ee0d2d00e 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -245,6 +245,12 @@ impl Decode for MultiAssets { } } +impl From> for MultiAssets { + fn from(x: Vec) -> Self { + Self(x) + } +} + impl From for Vec { fn from(a: MultiAssets) -> Self { a.0.into_iter().map(OldMultiAsset::from).collect() @@ -397,6 +403,12 @@ pub enum MultiAssetFilter { Wild(WildMultiAsset), } +impl From for MultiAssetFilter { + fn from(x: WildMultiAsset) -> Self { + Self::Wild(x) + } +} + impl From for Vec { fn from(a: MultiAssetFilter) -> Self { use MultiAssetFilter::*; diff --git a/xcm/xcm-builder/src/weight.rs b/xcm/xcm-builder/src/weight.rs index e2096afcaa30..26f282abfec7 100644 --- a/xcm/xcm-builder/src/weight.rs +++ b/xcm/xcm-builder/src/weight.rs @@ -110,14 +110,13 @@ impl, R: TakeRevenue> WeightTrader for FixedRateOf Ok(unused) } - fn refund_weight(&mut self, weight: Weight) -> MultiAsset { + fn refund_weight(&mut self, weight: Weight) -> Option { let (id, units_per_second) = T::get(); let weight = weight.min(self.0); let amount = units_per_second * (weight as u128) / 1_000_000_000_000u128; self.0 -= weight; self.1 = self.1.saturating_sub(amount); - let result = MultiAsset::ConcreteFungible { amount, id }; - result + Some((id, amount).into()) } } @@ -158,16 +157,12 @@ impl< Ok(unused) } - fn refund_weight(&mut self, weight: Weight) -> MultiAsset { + fn refund_weight(&mut self, weight: Weight) -> Option { let weight = weight.min(self.0); let amount = WeightToFee::calc(&weight); self.0 -= weight; self.1 = self.1.saturating_sub(amount); - let result = MultiAsset::ConcreteFungible { - amount: amount.saturated_into(), - id: AssetId::get(), - }; - result + Some(MultiAsset::from((AssetId::get(), amount.saturated_into()))) } } diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 59a6b9fd81e6..7c2b5410991c 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -16,10 +16,8 @@ use sp_std::{prelude::*, mem, collections::{btree_map::BTreeMap, btree_set::BTreeSet}}; use xcm::v0::{ - MultiAsset, MultiAssets, MultiLocation, AssetInstance, - MultiAssetFilter::{self, Assets, Wild}, - AssetId::{self, Concrete, Abstract}, - WildMultiAsset::{self, All, AllOf}, + MultiAsset, MultiAssets, MultiLocation, AssetInstance, MultiAssetFilter, AssetId, + WildMultiAsset::{All, AllOf}, Fungibility::{Fungible, NonFungible}, WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible}, }; @@ -32,7 +30,7 @@ pub struct Assets { pub fungible: BTreeMap, /// The non-fungible assets. - // OPTIMIZE: Consider BTreeMap> + // TODO: Consider BTreeMap> // or even BTreeMap> pub non_fungible: BTreeSet<(AssetId, AssetInstance)>, } @@ -47,12 +45,24 @@ impl From> for Assets { } } +impl From for Assets { + fn from(assets: MultiAssets) -> Assets { + assets.drain().into() + } +} + impl From for Vec { fn from(a: Assets) -> Self { a.into_assets_iter().collect() } } +impl From for MultiAssets { + fn from(a: Assets) -> Self { + a.into_assets_iter().collect::>().into() + } +} + impl From for Assets { fn from(asset: MultiAsset) -> Assets { let mut result = Self::default(); @@ -62,6 +72,7 @@ impl From for Assets { } /// An error emitted by `take` operations. +#[derive(Debug)] pub enum TakeError { /// There was an attempt to take an asset without saturating (enough of) which did not exist. AssetUnderflow(MultiAsset), @@ -106,11 +117,15 @@ impl Assets { /// Wildcard values of `asset` do nothing. pub fn subsume(&mut self, asset: MultiAsset) { match asset.fun { - Fungible(amount) => self.fungible - .entry(asset.id) - .and_modify(|e| *e = e.saturating_add(amount)) - .or_insert(amount), - NonFungible(instance) => self.non_fungible.insert((asset.id, instance)), + Fungible(amount) => { + self.fungible + .entry(asset.id) + .and_modify(|e| *e = e.saturating_add(amount)) + .or_insert(amount); + } + NonFungible(instance) => { + self.non_fungible.insert((asset.id, instance)); + } } } @@ -128,12 +143,12 @@ impl Assets { let mut fungible = Default::default(); mem::swap(&mut self.fungible, &mut fungible); self.fungible = fungible.into_iter() - .map(|(mut id, amount)| { let _ = id.prepend_location(prepend); (id, amount) }) + .map(|(mut id, amount)| { let _ = id.reanchor(prepend); (id, amount) }) .collect(); let mut non_fungible = Default::default(); mem::swap(&mut self.non_fungible, &mut non_fungible); self.non_fungible = non_fungible.into_iter() - .map(|(mut class, inst)| { let _ = class.prepend_location(prepend); (class, inst) }) + .map(|(mut class, inst)| { let _ = class.reanchor(prepend); (class, inst) }) .collect(); } @@ -144,13 +159,13 @@ impl Assets { match asset { MultiAsset { fun: Fungible(ref amount), ref id } => { if self.fungible.get(id).map_or(true, |a| a < amount) { - return Err(TakeError((id.clone(), amount).into())) + return Err(TakeError::AssetUnderflow((id.clone(), *amount).into())) } } MultiAsset { fun: NonFungible(ref instance), ref id } => { let id_instance = (id.clone(), instance.clone()); if !self.non_fungible.contains(&id_instance) { - return Err(TakeError(id_instance.into())) + return Err(TakeError::AssetUnderflow(id_instance.into())) } } } @@ -162,46 +177,42 @@ impl Assets { /// /// If `saturate` is `true`, then `self` is considered to be masked by `mask`, thereby avoiding any attempt at /// reducing it by assets it does not contain. In this case, the function is infallible. If `saturate` is `false` - /// and `asset` references a definite asset which `self` does not contain then an error is returned. + /// and `mask` references a definite asset which `self` does not contain then an error is returned. /// /// Returns `Ok` with the definite assets token from `self` and mutates `self` to its value minus - /// `asset`. Returns `Err` in the non-saturating case where `self` did not contain (enough of) a definite asset to + /// `mask`. Returns `Err` in the non-saturating case where `self` did not contain (enough of) a definite asset to /// be removed. fn general_take(&mut self, mask: MultiAssetFilter, saturate: bool) -> Result { + let mut taken = Assets::new(); match mask { - Wild(All) => Ok(self.swapped(Assets::new())), - Wild(AllOf(WildFungible, id)) => { - let mut taken = Assets::new(); + MultiAssetFilter::Wild(All) => return Ok(self.swapped(Assets::new())), + MultiAssetFilter::Wild(AllOf(WildFungible, id)) => { if let Some((id, amount)) = self.fungible.remove_entry(&id) { taken.fungible.insert(id, amount); } - Ok(taken) } - Wild(AllOf(WildNonFungible, id)) => { - let mut taken = Assets::new(); + MultiAssetFilter::Wild(AllOf(WildNonFungible, id)) => { let non_fungible = mem::replace(&mut self.non_fungible, Default::default()); non_fungible.into_iter().for_each(|(c, instance)| { - if &c == id { + if c == id { taken.non_fungible.insert((c, instance)); } else { self.non_fungible.insert((c, instance)); } }); - Ok(taken) } - Assets(assets) => { + MultiAssetFilter::Assets(assets) => { if !saturate { self.ensure_contains(&assets)?; } - let mut taken = Assets::new(); for asset in assets.drain().into_iter() { match asset { - MultiAsset { fun: Fungible(mut amount), id } => { + MultiAsset { fun: Fungible(amount), id } => { let (remove, amount) = match self.fungible.get_mut(&id) { Some(self_amount) => { let amount = amount.min(*self_amount); *self_amount -= amount; - (self_amount == 0, amount) + (*self_amount == 0, amount) } None => (false, 0), }; @@ -222,32 +233,30 @@ impl Assets { } } } + Ok(taken) } - /// Mutates `self` to its original value less `asset` and returns `true` iff it contains at least `asset`. - /// - /// `asset` is interpreted as being masked under `self`. + /// Mutates `self` to its original value less `mask` and returns `true` iff it contains at least `mask`. /// - /// Returns `Ok` with the non-wildcard equivalence of `asset` taken and mutates `self` to its value minus - /// `asset` if `self` contains `asset`, and return `Err` otherwise. + /// Returns `Ok` with the non-wildcard equivalence of `mask` taken and mutates `self` to its value minus + /// `mask` if `self` contains `asset`, and return `Err` otherwise. pub fn saturating_take(&mut self, asset: MultiAssetFilter) -> Assets { - self.general_take(asset, true).expect("general_take never results in error when saturating") + self.general_take(asset, true) + .expect("general_take never results in error when saturating") } - /// Mutates `self` to its original value less `asset` and returns `true` iff it contains at least `asset`. - /// - /// `asset` is interpreted as being masked under `self`. + /// Mutates `self` to its original value less `mask` and returns `true` iff it contains at least `mask`. /// /// Returns `Ok` with the non-wildcard equivalence of `asset` taken and mutates `self` to its value minus /// `asset` if `self` contains `asset`, and return `Err` otherwise. - pub fn try_take(&mut self, asset: MultiAssetFilter) -> Result { - self.general_take(asset, false) + pub fn try_take(&mut self, mask: MultiAssetFilter) -> Result { + self.general_take(mask, false) } - /// Consumes `self` and returns its original value excluding `asset` iff it contains at least `asset`, as well as + /// Consumes `self` and returns its original value excluding `mask` iff it contains at least `mask`, as well as /// the assets excluded. - pub fn less(mut self, asset: WildMultiAsset) -> Result<(Assets, Assets), Self> { - match self.try_take(asset) { + pub fn less(mut self, mask: MultiAssetFilter) -> Result<(Assets, Assets), Self> { + match self.try_take(mask) { Ok(taken) => Ok((self, taken)), Err(_) => Err(self), } @@ -277,23 +286,23 @@ impl Assets { /// MultiAsset::AbstractFungible { id: vec![0], amount: 50 }, /// ]); /// ``` - pub fn min(&self, mask: MultiAssetFilter) -> Assets { + pub fn min(&self, mask: &MultiAssetFilter) -> Assets { let mut masked = Assets::new(); match mask { - Wild(All) => Ok(self.clone()), - Wild(AllOf(WildFungible, id)) => { - if let Some(&(ref id, amount)) = self.fungible.get(&id) { + MultiAssetFilter::Wild(All) => return self.clone(), + MultiAssetFilter::Wild(AllOf(WildFungible, id)) => { + if let Some(&amount) = self.fungible.get(&id) { masked.fungible.insert(id.clone(), amount); } } - Wild(AllOf(WildNonFungible, id)) => { + MultiAssetFilter::Wild(AllOf(WildNonFungible, id)) => { self.non_fungible.iter().for_each(|(ref c, ref instance)| { if c == id { masked.non_fungible.insert((c.clone(), instance.clone())); } }); } - Assets(assets) => { + MultiAssetFilter::Assets(assets) => { for asset in assets.inner().iter() { match asset { MultiAsset { fun: Fungible(ref amount), ref id } => { diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 87b770533db2..b9ec7b4a6e92 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -21,10 +21,7 @@ use frame_support::{ ensure, weights::GetDispatchInfo, dispatch::{Weight, Dispatchable} }; -use xcm::v0::{ - ExecuteXcm, SendXcm, Error as XcmError, Outcome, - MultiLocation, MultiAsset, Xcm, Order, Response, -}; +use xcm::v0::{ExecuteXcm, SendXcm, Error as XcmError, Outcome, MultiLocation, MultiAssets, Xcm, Order, Response}; pub mod traits; use traits::{ @@ -33,7 +30,7 @@ use traits::{ }; mod assets; -pub use assets::{Assets, AssetId}; +pub use assets::Assets; mod config; pub use config::Config; @@ -86,10 +83,10 @@ impl ExecuteXcm for XcmExecutor { } impl XcmExecutor { - fn reanchored(mut assets: Assets, dest: &MultiLocation) -> Vec { + fn reanchored(mut assets: Assets, dest: &MultiLocation) -> MultiAssets { let inv_dest = Config::LocationInverter::invert_location(&dest); assets.prepend_location(&inv_dest); - assets.into_assets_iter().collect::>() + assets.into_assets_iter().collect::>().into() } /// Execute the XCM and return the portion of weight of `shallow_weight + deep_weight` that `message` did not use. @@ -130,8 +127,7 @@ impl XcmExecutor { (origin, Xcm::WithdrawAsset { assets, effects }) => { // Take `assets` from the origin account (on-chain) and place in holding. let mut holding = Assets::default(); - for asset in assets { - ensure!(!asset.is_wildcard(), XcmError::Wildcard); + for asset in assets.inner() { let withdrawn = Config::AssetTransactor::withdraw_asset(&asset, &origin)?; holding.subsume_assets(withdrawn); } @@ -139,18 +135,16 @@ impl XcmExecutor { } (origin, Xcm::ReserveAssetDeposit { assets, effects }) => { // check whether we trust origin to be our reserve location for this asset. - for asset in assets.iter() { - ensure!(!asset.is_wildcard(), XcmError::Wildcard); + for asset in assets.inner() { // We only trust the origin to send us assets that they identify as their // sovereign assets. ensure!(Config::IsReserve::filter_asset_location(asset, &origin), XcmError::UntrustedReserveLocation); } - Some((Assets::from(assets), effects)) + Some((assets.into(), effects)) } (origin, Xcm::TransferAsset { assets, dest }) => { // Take `assets` from the origin account (on-chain) and place into dest account. - for asset in assets { - ensure!(!asset.is_wildcard(), XcmError::Wildcard); + for asset in assets.inner() { Config::AssetTransactor::teleport_asset(&asset, &origin, &dest)?; } None @@ -158,18 +152,16 @@ impl XcmExecutor { (origin, Xcm::TransferReserveAsset { mut assets, dest, effects }) => { // Take `assets` from the origin account (on-chain) and place into dest account. let inv_dest = Config::LocationInverter::invert_location(&dest); - for asset in assets.iter_mut() { - ensure!(!asset.is_wildcard(), XcmError::Wildcard); - Config::AssetTransactor::teleport_asset(&asset, &origin, &dest)?; - asset.reanchor(&inv_dest)?; + for asset in assets.inner() { + Config::AssetTransactor::teleport_asset(asset, &origin, &dest)?; } + assets.reanchor(&inv_dest)?; Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposit { assets, effects })?; None } (origin, Xcm::TeleportAsset { assets, effects }) => { // check whether we trust origin to teleport this asset to us via config trait. - for asset in assets.iter() { - ensure!(!asset.is_wildcard(), XcmError::Wildcard); + for asset in assets.inner() { // We only trust the origin to send us assets that they identify as their // sovereign assets. ensure!(Config::IsTeleporter::filter_asset_location(asset, &origin), XcmError::UntrustedTeleportLocation); @@ -178,7 +170,7 @@ impl XcmExecutor { // don't want to punish a possibly innocent chain/user). Config::AssetTransactor::can_check_in(&origin, asset)?; } - for asset in assets.iter() { + for asset in assets.inner() { Config::AssetTransactor::check_in(&origin, asset); } Some((Assets::from(assets), effects)) @@ -280,13 +272,13 @@ impl XcmExecutor { Config::XcmSender::send_xcm(dest, Xcm::TeleportAsset { assets, effects })?; } Order::QueryHolding { query_id, dest, assets } => { - let assets = Self::reanchored(holding.min(assets.iter()), &dest); + let assets = Self::reanchored(holding.min(&assets), &dest); Config::XcmSender::send_xcm(dest, Xcm::QueryResponse { query_id, response: Response::Assets(assets) })?; } Order::BuyExecution { fees, weight, debt, halt_on_error, xcm } => { // pay for `weight` using up to `fees` of the holding account. let purchasing_weight = Weight::from(weight.checked_add(debt).ok_or(XcmError::Overflow)?); - let max_fee = holding.try_take(fees).map_err(|()| XcmError::NotHoldingFees)?; + let max_fee = holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?; let unspent = trader.buy_weight(purchasing_weight, max_fee)?; holding.subsume_assets(unspent); @@ -298,7 +290,9 @@ impl XcmExecutor { Ok(surplus) => { total_surplus += surplus } } } - holding.subsume(trader.refund_weight(remaining_weight)); + if let Some(w) = trader.refund_weight(remaining_weight) { + holding.subsume(w); + } } _ => return Err(XcmError::UnhandledEffect)?, } diff --git a/xcm/xcm-executor/src/traits/transact_asset.rs b/xcm/xcm-executor/src/traits/transact_asset.rs index a967f1f6909a..610f98977ca7 100644 --- a/xcm/xcm-executor/src/traits/transact_asset.rs +++ b/xcm/xcm-executor/src/traits/transact_asset.rs @@ -103,7 +103,7 @@ impl TransactAsset for Tuple { fn can_check_in(origin: &MultiLocation, what: &MultiAsset) -> XcmResult { for_tuples!( #( match Tuple::can_check_in(origin, what) { - Err(XcmError::AssetNotFound | XcmError::Unimplemented) => (), + Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (), r => return r, } )* ); @@ -131,7 +131,7 @@ impl TransactAsset for Tuple { fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> XcmResult { for_tuples!( #( match Tuple::deposit_asset(what, who) { - Err(XcmError::AssetNotFound | XcmError::Unimplemented) => (), + Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (), r => return r, } )* ); @@ -147,7 +147,7 @@ impl TransactAsset for Tuple { fn withdraw_asset(what: &MultiAsset, who: &MultiLocation) -> Result { for_tuples!( #( match Tuple::withdraw_asset(what, who) { - Err(XcmError::AssetNotFound | XcmError::Unimplemented) => (), + Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (), r => return r, } )* ); @@ -163,7 +163,7 @@ impl TransactAsset for Tuple { fn transfer_asset(what: &MultiAsset, from: &MultiLocation, to: &MultiLocation) -> Result { for_tuples!( #( match Tuple::transfer_asset(what, from, to) { - Err(XcmError::AssetNotFound | XcmError::Unimplemented) => (), + Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (), r => return r, } )* ); diff --git a/xcm/xcm-executor/src/traits/weight.rs b/xcm/xcm-executor/src/traits/weight.rs index dc9589803af5..1844f3b6c7f2 100644 --- a/xcm/xcm-executor/src/traits/weight.rs +++ b/xcm/xcm-executor/src/traits/weight.rs @@ -71,7 +71,7 @@ pub trait WeightTrader: Sized { /// purchased using `buy_weight`. /// /// Default implementation refunds nothing. - fn refund_weight(&mut self, _weight: Weight) -> MultiAsset { MultiAsset::None } + fn refund_weight(&mut self, _weight: Weight) -> Option { None } } impl WeightTrader for () { From 8017453bf8ae1edb0c9a3b77c49d04187c079afe Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 31 Jul 2021 11:39:28 +0200 Subject: [PATCH 05/84] XCM Builder builds --- xcm/src/v0/multiasset.rs | 42 +++++++++++++------- xcm/xcm-builder/src/filter_asset_location.rs | 4 +- xcm/xcm-builder/src/fungibles_adapter.rs | 13 +++--- xcm/xcm-builder/src/matches_fungible.rs | 10 ++--- xcm/xcm-builder/src/weight.rs | 21 +++++----- xcm/xcm-executor/src/assets.rs | 10 +++++ 6 files changed, 61 insertions(+), 39 deletions(-) diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index 3d0ee0d2d00e..c877f71a0dc6 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -64,6 +64,18 @@ pub enum AssetId { Abstract(Vec), } +impl From for AssetId { + fn from(x: MultiLocation) -> Self { + Self::Concrete(x) + } +} + +impl From> for AssetId { + fn from(x: Vec) -> Self { + Self::Abstract(x) + } +} + impl AssetId { /// Prepend a `MultiLocation` to a concrete asset, giving it a new root location. pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { @@ -137,21 +149,9 @@ impl Ord for MultiAsset { } } -impl From<(AssetId, Fungibility)> for MultiAsset { - fn from((id, fun): (AssetId, Fungibility)) -> MultiAsset { - MultiAsset { fun, id } - } -} - -impl From<(AssetId, u128)> for MultiAsset { - fn from((id, amount): (AssetId, u128)) -> MultiAsset { - MultiAsset { fun: amount.into(), id } - } -} - -impl From<(AssetId, AssetInstance)> for MultiAsset { - fn from((id, instance): (AssetId, AssetInstance)) -> MultiAsset { - MultiAsset { fun: instance.into(), id } +impl, B: Into> From<(A, B)> for MultiAsset { + fn from((id, fun): (A, B)) -> MultiAsset { + MultiAsset { fun: fun.into(), id: id.into() } } } @@ -409,6 +409,18 @@ impl From for MultiAssetFilter { } } +impl From for MultiAssetFilter { + fn from(x: MultiAsset) -> Self { + Self::Assets(vec![x].into()) + } +} + +impl From for MultiAssetFilter { + fn from(x: MultiAssets) -> Self { + Self::Assets(x) + } +} + impl From for Vec { fn from(a: MultiAssetFilter) -> Self { use MultiAssetFilter::*; diff --git a/xcm/xcm-builder/src/filter_asset_location.rs b/xcm/xcm-builder/src/filter_asset_location.rs index 9ad4f40aa71c..e095b9dbbc73 100644 --- a/xcm/xcm-builder/src/filter_asset_location.rs +++ b/xcm/xcm-builder/src/filter_asset_location.rs @@ -17,7 +17,7 @@ //! Various implementations of `FilterAssetLocation`. use sp_std::marker::PhantomData; -use xcm::v0::{MultiAsset, MultiLocation}; +use xcm::v0::{MultiAsset, MultiLocation, AssetId::Concrete}; use frame_support::traits::Get; use xcm_executor::traits::FilterAssetLocation; @@ -25,7 +25,7 @@ use xcm_executor::traits::FilterAssetLocation; pub struct NativeAsset; impl FilterAssetLocation for NativeAsset { fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { - matches!(asset, MultiAsset::ConcreteFungible { ref id, .. } if id == origin) + matches!(asset.id, Concrete(ref id) if id == origin) } } diff --git a/xcm/xcm-builder/src/fungibles_adapter.rs b/xcm/xcm-builder/src/fungibles_adapter.rs index 80cd74c0124d..121d0828f165 100644 --- a/xcm/xcm-builder/src/fungibles_adapter.rs +++ b/xcm/xcm-builder/src/fungibles_adapter.rs @@ -17,7 +17,10 @@ //! Adapters to work with `frame_support::traits::tokens::fungibles` through XCM. 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, Fungibility::Fungible, + AssetId::{Concrete, Abstract}, +}; use frame_support::traits::{Get, tokens::fungibles, Contains}; use xcm_executor::traits::{TransactAsset, Convert, MatchesFungibles, Error as MatchError}; @@ -61,8 +64,8 @@ impl< ConvertedConcreteAssetId { fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> { - let (id, amount) = match a { - MultiAsset::ConcreteFungible { id, amount } => (id, amount), + let (amount, id) = match (&a.fun, &a.id) { + (Fungible(ref amount), Concrete(ref id)) => (amount, id), _ => return Err(MatchError::AssetNotFound), }; let what = ConvertAssetId::convert_ref(id).map_err(|_| MatchError::AssetIdConversionFailed)?; @@ -83,8 +86,8 @@ impl< ConvertedAbstractAssetId { fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> { - let (id, amount) = match a { - MultiAsset::AbstractFungible { id, amount } => (id, amount), + let (amount, id) = match (&a.fun, &a.id) { + (Fungible(ref amount), Abstract(ref id)) => (amount, id), _ => return Err(MatchError::AssetNotFound), }; let what = ConvertAssetId::convert_ref(id).map_err(|_| MatchError::AssetIdConversionFailed)?; diff --git a/xcm/xcm-builder/src/matches_fungible.rs b/xcm/xcm-builder/src/matches_fungible.rs index 4d76a49b6bd8..8ec1893ee9df 100644 --- a/xcm/xcm-builder/src/matches_fungible.rs +++ b/xcm/xcm-builder/src/matches_fungible.rs @@ -18,7 +18,7 @@ use sp_std::{marker::PhantomData, convert::TryFrom}; use sp_runtime::traits::CheckedConversion; -use xcm::v0::{MultiAsset, MultiLocation}; +use xcm::v0::{MultiAsset, MultiLocation, AssetId::{Abstract, Concrete}, Fungibility::Fungible}; use frame_support::traits::Get; use xcm_executor::traits::MatchesFungible; @@ -46,8 +46,8 @@ use xcm_executor::traits::MatchesFungible; pub struct IsConcrete(PhantomData); impl, B: TryFrom> MatchesFungible for IsConcrete { fn matches_fungible(a: &MultiAsset) -> Option { - match a { - MultiAsset::ConcreteFungible { id, amount } if id == &T::get() => + match (&a.id, &a.fun) { + (Concrete(ref id), Fungible(ref amount)) if id == &T::get() => CheckedConversion::checked_from(*amount), _ => None, } @@ -76,8 +76,8 @@ impl, B: TryFrom> MatchesFungible for IsConcrete< pub struct IsAbstract(PhantomData); impl, B: TryFrom> MatchesFungible for IsAbstract { fn matches_fungible(a: &MultiAsset) -> Option { - match a { - MultiAsset::AbstractFungible { id, amount } if &id[..] == T::get() => + match (&a.id, &a.fun) { + (Abstract(ref id), Fungible(ref amount)) if id == &T::get() => CheckedConversion::checked_from(*amount), _ => None, } diff --git a/xcm/xcm-builder/src/weight.rs b/xcm/xcm-builder/src/weight.rs index 26f282abfec7..77f28c73f571 100644 --- a/xcm/xcm-builder/src/weight.rs +++ b/xcm/xcm-builder/src/weight.rs @@ -16,7 +16,7 @@ use sp_std::{result::Result, marker::PhantomData, convert::TryInto}; use parity_scale_codec::Decode; -use xcm::v0::{Xcm, Order, MultiAsset, MultiLocation, Error}; +use xcm::v0::{Xcm, Order, MultiAsset, MultiLocation, Error, AssetId::Concrete}; use sp_runtime::traits::{Zero, Saturating, SaturatedConversion}; use frame_support::traits::{Get, OnUnbalanced as OnUnbalancedT, tokens::currency::Currency as CurrencyT}; use frame_support::weights::{Weight, GetDispatchInfo, WeightToFeePolynomial}; @@ -103,8 +103,7 @@ impl, R: TakeRevenue> WeightTrader for FixedRateOf let (id, units_per_second) = T::get(); use frame_support::weights::constants::WEIGHT_PER_SECOND; let amount = units_per_second * (weight as u128) / (WEIGHT_PER_SECOND as u128); - let required = MultiAsset::ConcreteFungible { amount, id }; - let (unused, _) = payment.less(required).map_err(|_| Error::TooExpensive)?; + let unused = payment.checked_sub((id, amount).into()).map_err(|_| Error::TooExpensive)?; self.0 = self.0.saturating_add(weight); self.1 = self.1.saturating_add(amount); Ok(unused) @@ -116,14 +115,13 @@ impl, R: TakeRevenue> WeightTrader for FixedRateOf let amount = units_per_second * (weight as u128) / 1_000_000_000_000u128; self.0 -= weight; self.1 = self.1.saturating_sub(amount); - Some((id, amount).into()) + Some((Concrete(id), amount).into()) } } impl, R: TakeRevenue> Drop for FixedRateOfConcreteFungible { fn drop(&mut self) { - let revenue = MultiAsset::ConcreteFungible { amount: self.1, id: T::get().0 }; - R::take_revenue(revenue); + R::take_revenue((Concrete(T::get().0), self.1).into()); } } @@ -147,11 +145,9 @@ impl< fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { let amount = WeightToFee::calc(&weight); - let required = MultiAsset::ConcreteFungible { - amount: amount.try_into().map_err(|_| Error::Overflow)?, - id: AssetId::get(), - }; - let (unused, _) = payment.less(required).map_err(|_| Error::TooExpensive)?; + let u128_amount: u128 = amount.try_into().map_err(|_| Error::Overflow)?; + let required = (Concrete(AssetId::get()), u128_amount).into(); + let unused = payment.checked_sub(required).map_err(|_| Error::TooExpensive)?; self.0 = self.0.saturating_add(weight); self.1 = self.1.saturating_add(amount); Ok(unused) @@ -162,7 +158,8 @@ impl< let amount = WeightToFee::calc(&weight); self.0 -= weight; self.1 = self.1.saturating_sub(amount); - Some(MultiAsset::from((AssetId::get(), amount.saturated_into()))) + let amount: u128 = amount.saturated_into(); + Some((AssetId::get(), amount).into()) } } diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 7c2b5410991c..b25b0b3e4637 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -253,6 +253,16 @@ impl Assets { self.general_take(mask, false) } + /// Consumes `self` and returns its original value excluding `asset` iff it contains at least `asset`. + pub fn checked_sub(mut self, asset: MultiAsset) -> Result { + // TODO: Optimize by doing this operation directly rather than converting into a MultiAssetFilter and + // constructing the unused `_taken` return value. + match self.try_take(asset.into()) { + Ok(_taken) => Ok(self), + Err(_) => Err(self), + } + } + /// Consumes `self` and returns its original value excluding `mask` iff it contains at least `mask`, as well as /// the assets excluded. pub fn less(mut self, mask: MultiAssetFilter) -> Result<(Assets, Assets), Self> { From fde986a2c55a000faf8f031b0dbfd66954c8eed7 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Sun, 1 Aug 2021 18:59:32 +0200 Subject: [PATCH 06/84] init --- rust-toolchain | 5 ++ xcm/xcm-executor/src/lib.rs | 1 + xcm/xcm-simulator/example/src/lib.rs | 64 +++++++++++++++++++- xcm/xcm-simulator/example/src/relay_chain.rs | 2 +- 4 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 rust-toolchain diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 000000000000..ae16315aa35e --- /dev/null +++ b/rust-toolchain @@ -0,0 +1,5 @@ +[toolchain] +channel = "nightly-2021-02-24" +components = [ "rustfmt", "clippy" ] +targets = [ "wasm32-unknown-unknown" ] +profile = "minimal" diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 8f8a5c9ee617..2760bb23999e 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -15,6 +15,7 @@ // along with Polkadot. If not, see . #![cfg_attr(not(feature = "std"), no_std)] +#![feature(or_patterns)] // revert before PRing use sp_std::{prelude::*, marker::PhantomData}; use frame_support::{ diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index f318409bf187..b0d999dbc5e1 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -19,6 +19,8 @@ mod relay_chain; use sp_runtime::AccountId32; use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; +use polkadot_parachain::primitives::Id as ParaId; +use sp_runtime::traits::AccountIdConversion; pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]); @@ -79,10 +81,10 @@ pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { pub fn relay_ext() -> sp_io::TestExternalities { use relay_chain::{Runtime, System}; + let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - - pallet_balances::GenesisConfig:: { balances: vec![(ALICE, INITIAL_BALANCE)] } + pallet_balances::GenesisConfig:: { balances: vec![(ALICE, INITIAL_BALANCE), (para_account_a, INITIAL_BALANCE)] } .assimilate_storage(&mut t) .unwrap(); @@ -99,13 +101,14 @@ mod tests { use super::*; use codec::Encode; - use frame_support::assert_ok; + use frame_support::{assert_ok, weights::Weight}; use xcm::v0::{ Junction::{self, Parachain, Parent}, MultiAsset::*, MultiLocation::*, NetworkId, OriginKind, Xcm::*, + Order, }; use xcm_simulator::TestExt; @@ -212,4 +215,59 @@ mod tests { ); }); } + + // Helper function for forming buy execution message + fn buy_execution(debt: Weight) -> Order { + use xcm::opaque::v0::prelude::*; + Order::BuyExecution { fees: All, weight: 0, debt, halt_on_error: false, xcm: vec![] } + } + + fn last_relay_chain_event() -> relay_chain::Event { + relay_chain::System::events().pop().expect("Event expected").event + } + + fn last_parachain_event() -> parachain::Event { + parachain::System::events().pop().expect("Event expected").event + } + + /// Scenario: + /// A parachain transfers funds on the relaychain to another parachain's account. + /// + /// Asserts that the parachain accounts are updated as expected. + #[test] + fn withdraw_and_deposit() { + use xcm::opaque::v0::prelude::*; + use sp_runtime::traits::AccountIdConversion; + use polkadot_parachain::primitives::Id as ParaId; + MockNet::reset(); + + ParaA::execute_with(|| { + let amount = 10; + let weight = 3 * relay_chain::BaseXcmWeight::get(); + let message = WithdrawAsset { + assets: vec![ConcreteFungible { id: Null, amount }], + effects: vec![ + buy_execution(weight), + Order::DepositAsset { + assets: vec![All], + dest: Parachain(2).into(), + }, + ], + }; + // Send withdraw and deposit + assert_ok!(ParachainPalletXcm::send_xcm( + Null, + X1(Parent), + message.clone(), + )); + }); + + Relay::execute_with(|| { + let amount = 10; + let para_account_one: relay_chain::AccountId = ParaId::from(1).into_account(); + assert_eq!(relay_chain::Balances::free_balance(para_account_one), INITIAL_BALANCE - amount); + let para_account: relay_chain::AccountId = ParaId::from(2).into_account(); + assert_eq!(relay_chain::Balances::free_balance(para_account), 10); + }); + } } diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index c69f20d05eaf..486054f5feeb 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -143,7 +143,7 @@ impl pallet_xcm::Config for Runtime { type XcmRouter = XcmRouter; // Anyone can execute XCM messages locally... type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; - type XcmExecuteFilter = (); + type XcmExecuteFilter = All<(MultiLocation, xcm::v0::Xcm)>; type XcmExecutor = XcmExecutor; type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; From f806a95bcb42727916a03ca0f62339fe7e468bb7 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Sun, 1 Aug 2021 19:27:00 +0200 Subject: [PATCH 07/84] reserve trasfer asset and query holding tests without message checking --- xcm/xcm-simulator/example/src/lib.rs | 130 ++++++++++++++++++++++----- 1 file changed, 106 insertions(+), 24 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index b0d999dbc5e1..1ef731faf88f 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -17,10 +17,10 @@ mod parachain; mod relay_chain; -use sp_runtime::AccountId32; -use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; use polkadot_parachain::primitives::Id as ParaId; -use sp_runtime::traits::AccountIdConversion; +use sp_runtime::{traits::AccountIdConversion, AccountId32}; +use xcm::opaque::v0::prelude::*; +use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]); @@ -84,9 +84,11 @@ pub fn relay_ext() -> sp_io::TestExternalities { let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { balances: vec![(ALICE, INITIAL_BALANCE), (para_account_a, INITIAL_BALANCE)] } - .assimilate_storage(&mut t) - .unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![(ALICE, INITIAL_BALANCE), (para_account_a, INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(|| System::set_block_number(1)); @@ -106,9 +108,8 @@ mod tests { Junction::{self, Parachain, Parent}, MultiAsset::*, MultiLocation::*, - NetworkId, OriginKind, + NetworkId, Order, OriginKind, Xcm::*, - Order, }; use xcm_simulator::TestExt; @@ -193,8 +194,12 @@ mod tests { }); } + /// Scenario: + /// A user Alice sends funds from the relaychain to a parachain. + /// + /// Asserts that the correct XCM is sent and the balances are set as expected. #[test] - fn reserve_transfer() { + fn reserve_transfer_assets() { MockNet::reset(); Relay::execute_with(|| { @@ -205,6 +210,8 @@ mod tests { vec![ConcreteFungible { id: Null, amount: 123 }], 123, )); + let para_account_a = ParaId::from(1).into_account(); + assert_eq!(parachain::Balances::free_balance(¶_account_a), INITIAL_BALANCE + 123); }); ParaA::execute_with(|| { @@ -236,38 +243,113 @@ mod tests { /// Asserts that the parachain accounts are updated as expected. #[test] fn withdraw_and_deposit() { - use xcm::opaque::v0::prelude::*; - use sp_runtime::traits::AccountIdConversion; - use polkadot_parachain::primitives::Id as ParaId; MockNet::reset(); ParaA::execute_with(|| { let amount = 10; let weight = 3 * relay_chain::BaseXcmWeight::get(); + let message = WithdrawAsset { + assets: vec![ConcreteFungible { id: Null, amount }], + effects: vec![ + buy_execution(weight), + Order::DepositAsset { assets: vec![All], dest: Parachain(2).into() }, + ], + }; + // Send withdraw and deposit + assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); + }); + + Relay::execute_with(|| { + let amount = 10; + let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); + assert_eq!( + relay_chain::Balances::free_balance(para_account_a), + INITIAL_BALANCE - amount + ); + let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); + assert_eq!(relay_chain::Balances::free_balance(para_account_b), 10); + }); + } + + /// Scenario: + /// A parachain wants to be notified that a transfer worked correctly. + /// It sends a `QueryHolding` after the deposit to get notified on success. + /// + /// Asserts that the balances are updated correctly and the expected XCM is sent. + #[test] + fn query_holding() { + MockNet::reset(); + + // First send a message which fails on the relay chain + ParaA::execute_with(|| { + let amount = 10; + let weight = 3 * relay_chain::BaseXcmWeight::get(); + //let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); + let query_id = 1234; let message = WithdrawAsset { assets: vec![ConcreteFungible { id: Null, amount }], effects: vec![ buy_execution(weight), Order::DepositAsset { assets: vec![All], - dest: Parachain(2).into(), + dest: OnlyChild.into(), // invalid destination }, + // is not triggered becasue the deposit fails + Order::QueryHolding { query_id, dest: Parachain(2).into(), assets: vec![All] }, ], }; - // Send withdraw and deposit - assert_ok!(ParachainPalletXcm::send_xcm( - Null, - X1(Parent), - message.clone(), - )); + // Send withdraw and deposit with query holding + assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); }); - + + // Check that no transfer was executed and no response message was sent Relay::execute_with(|| { let amount = 10; - let para_account_one: relay_chain::AccountId = ParaId::from(1).into_account(); - assert_eq!(relay_chain::Balances::free_balance(para_account_one), INITIAL_BALANCE - amount); - let para_account: relay_chain::AccountId = ParaId::from(2).into_account(); - assert_eq!(relay_chain::Balances::free_balance(para_account), 10); + let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); + // withdraw did execute + assert_eq!( + relay_chain::Balances::free_balance(para_account_a), + INITIAL_BALANCE - amount + ); + // but deposit did not execute + let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); + assert_eq!(relay_chain::Balances::free_balance(para_account_b), 0); + // TODO: verify no message was sent, but mock XCM router is not mock... + }); + + // Now send a message which fully succeeds on the relay chain + ParaA::execute_with(|| { + let amount = 10; + let weight = 3 * relay_chain::BaseXcmWeight::get(); + let query_id = 1234; + let message = WithdrawAsset { + assets: vec![ConcreteFungible { id: Null, amount }], + effects: vec![ + buy_execution(weight), + Order::DepositAsset { + assets: vec![All], + dest: Parachain(2).into(), // valid destination + }, + Order::QueryHolding { query_id, dest: Parachain(2).into(), assets: vec![All] }, + ], + }; + // Send withdraw and deposit with query holding + assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); + }); + + // Check that transfer was executed and response message was sent + Relay::execute_with(|| { + let spent = 20; + let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); + // withdraw did execute + assert_eq!( + relay_chain::Balances::free_balance(para_account_a), + INITIAL_BALANCE - spent + ); + // and deposit did execute + let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); + assert_eq!(relay_chain::Balances::free_balance(para_account_b), 10); + // TODO: check the sent message is a QueryResponse to the Parachain(1) }); } } From eb4224be40fc63a729caa6711404e95d22397e77 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Sun, 1 Aug 2021 19:56:22 +0200 Subject: [PATCH 08/84] XcmSender short circuits on first successful impl for tuples apparently --- xcm/xcm-simulator/example/src/lib.rs | 4 ++-- xcm/xcm-simulator/example/src/relay_chain.rs | 23 ++++++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 1ef731faf88f..bdff2de239d1 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -314,7 +314,7 @@ mod tests { // but deposit did not execute let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); assert_eq!(relay_chain::Balances::free_balance(para_account_b), 0); - // TODO: verify no message was sent, but mock XCM router is not mock... + assert_eq!(relay_chain::sent_xcm(), vec![]); }); // Now send a message which fully succeeds on the relay chain @@ -349,7 +349,7 @@ mod tests { // and deposit did execute let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); assert_eq!(relay_chain::Balances::free_balance(para_account_b), 10); - // TODO: check the sent message is a QueryResponse to the Parachain(1) + assert_eq!(relay_chain::sent_xcm(), vec![]); }); } } diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index 486054f5feeb..43ed0d39ce9b 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -26,7 +26,11 @@ use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; use polkadot_parachain::primitives::Id as ParaId; use polkadot_runtime_parachains::{configuration, origin, shared, ump}; -use xcm::v0::{MultiAsset, MultiLocation, NetworkId}; +use sp_std::cell::RefCell; +use xcm::{ + opaque::v0::{Result as XcmResult, SendXcm, Xcm}, + v0::{MultiAsset, MultiLocation, NetworkId}, +}; use xcm_builder::{ AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, @@ -120,10 +124,25 @@ parameter_types! { pub type XcmRouter = super::RelayChainXcmRouter; pub type Barrier = AllowUnpaidExecutionFrom>; +thread_local! { + pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); +} +pub fn sent_xcm() -> Vec<(MultiLocation, Xcm)> { + SENT_XCM.with(|q| (*q.borrow()).clone()) +} +/// Sender that never returns error, always sends +pub struct TestSendXcm; +impl SendXcm for TestSendXcm { + fn send_xcm(dest: MultiLocation, msg: Xcm) -> XcmResult { + SENT_XCM.with(|q| q.borrow_mut().push((dest, msg))); + Ok(()) + } +} + pub struct XcmConfig; impl Config for XcmConfig { type Call = Call; - type XcmSender = XcmRouter; + type XcmSender = (TestSendXcm, XcmRouter); type AssetTransactor = LocalAssetTransactor; type OriginConverter = LocalOriginConverter; type IsReserve = (); From 5445d0675c6fabbed58ef02e6db55b01ed81449a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 1 Aug 2021 20:00:31 +0200 Subject: [PATCH 09/84] API changes making their way throughout --- runtime/kusama/src/lib.rs | 51 +-- runtime/rococo/src/lib.rs | 66 +--- runtime/westend/src/lib.rs | 57 +-- xcm/pallet-xcm/src/lib.rs | 20 +- xcm/src/v0/mod.rs | 22 +- xcm/src/v0/multi_asset.rs | 337 ------------------ xcm/src/v0/multiasset.rs | 171 ++------- xcm/src/v0/order.rs | 8 +- xcm/src/v0/traits.rs | 2 +- xcm/xcm-builder/src/barriers.rs | 8 +- xcm/xcm-builder/src/filter_asset_location.rs | 4 +- xcm/xcm-builder/src/tests.rs | 10 +- xcm/xcm-builder/src/weight.rs | 4 +- xcm/xcm-executor/src/assets.rs | 13 +- xcm/xcm-executor/src/lib.rs | 12 +- xcm/xcm-executor/src/traits/transact_asset.rs | 2 +- 16 files changed, 88 insertions(+), 699 deletions(-) delete mode 100644 xcm/src/v0/multi_asset.rs diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index 3d0ab1ebd099..8b307d392843 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -1307,61 +1307,14 @@ pub type LocalOriginToLocation = ( SignedToAccountId32, ); -pub struct OnlyWithdrawTeleportForAccounts; -impl frame_support::traits::Contains<(MultiLocation, Xcm)> for OnlyWithdrawTeleportForAccounts { - fn contains((ref origin, ref msg): &(MultiLocation, Xcm)) -> bool { - use xcm::v0::{ - Xcm::WithdrawAsset, Order::{BuyExecution, InitiateTeleport, DepositAsset}, - MultiAsset::{All, ConcreteFungible}, Junction::{AccountId32, Plurality}, - }; - match origin { - // Root and council are are allowed to execute anything. - Null | X1(Plurality { .. }) => true, - X1(AccountId32 { .. }) => { - // An account ID trying to send a message. We ensure that it's sensible. - // This checks that it's of the form: - // WithdrawAsset { - // assets: [ ConcreteFungible { id: Null } ], - // effects: [ BuyExecution, InitiateTeleport { - // assets: All, - // dest: Parachain, - // effects: [ BuyExecution, DepositAssets { - // assets: All, - // dest: AccountId32, - // } ] - // } ] - // } - matches!(msg, WithdrawAsset { ref assets, ref effects } - if assets.len() == 1 - && matches!(assets[0], ConcreteFungible { id: Null, .. }) - && effects.len() == 2 - && matches!(effects[0], BuyExecution { .. }) - && matches!(effects[1], InitiateTeleport { ref assets, dest: X1(Parachain(..)), ref effects } - if assets.len() == 1 - && matches!(assets[0], All) - && effects.len() == 2 - && matches!(effects[0], BuyExecution { .. }) - && matches!(effects[1], DepositAsset { ref assets, dest: X1(AccountId32{..}) } - if assets.len() == 1 - && matches!(assets[0], All) - ) - ) - ) - } - // Nobody else is allowed to execute anything. - _ => false, - } - } -} - impl pallet_xcm::Config for Runtime { type Event = Event; type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; type XcmRouter = XcmRouter; // Anyone can execute XCM messages locally... type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; - // ...but they must match our filter, which requires them to be a simple withdraw + teleport. - type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts; + // ...but they must match our filter, which rejects all. + type XcmExecuteFilter = (); type XcmExecutor = XcmExecutor; type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 790248daf644..7000c291a1ff 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -627,16 +627,13 @@ pub type XcmRouter = ( xcm_sender::ChildParachainRouter, ); -use xcm::v0::{MultiAsset, MultiAsset::AllConcreteFungible, MultiLocation::{Null, X1}, Junction::Parachain}; +use xcm::v0::prelude::*; parameter_types! { - pub const RococoForTick: (MultiAsset, MultiLocation) = - (AllConcreteFungible { id: Null }, X1(Parachain(100))); - pub const RococoForTrick: (MultiAsset, MultiLocation) = - (AllConcreteFungible { id: Null }, X1(Parachain(110))); - pub const RococoForTrack: (MultiAsset, MultiLocation) = - (AllConcreteFungible { id: Null }, X1(Parachain(120))); - pub const RococoForStatemint: (MultiAsset, MultiLocation) = - (AllConcreteFungible { id: Null }, X1(Parachain(1001))); + pub const Rococo: MultiAssetFilter = Wild(AllOf(Concrete(RocLocation::get()), WildFungible)); + pub const RococoForTick: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(100))); + pub const RococoForTrick: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(110))); + pub const RococoForTrack: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(120))); + pub const RococoForStatemint: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(1001))); } pub type TrustedTeleporters = ( xcm_builder::Case, @@ -691,61 +688,14 @@ pub type LocalOriginToLocation = ( SignedToAccountId32, ); -pub struct OnlyWithdrawTeleportForAccounts; -impl frame_support::traits::Contains<(MultiLocation, Xcm)> for OnlyWithdrawTeleportForAccounts { - fn contains((ref origin, ref msg): &(MultiLocation, Xcm)) -> bool { - use xcm::v0::{ - Xcm::WithdrawAsset, Order::{BuyExecution, InitiateTeleport, DepositAsset}, - MultiAsset::{All, ConcreteFungible}, Junction::{AccountId32, Plurality}, - }; - match origin { - // Root and collective are allowed to execute anything. - Null | X1(Plurality { .. }) => true, - X1(AccountId32 { .. }) => { - // An account ID trying to send a message. We ensure that it's sensible. - // This checks that it's of the form: - // WithdrawAsset { - // assets: [ ConcreteFungible { id: Null } ], - // effects: [ BuyExecution, InitiateTeleport { - // assets: All, - // dest: Parachain, - // effects: [ BuyExecution, DepositAssets { - // assets: All, - // dest: AccountId32, - // } ] - // } ] - // } - matches!(msg, WithdrawAsset { ref assets, ref effects } - if assets.len() == 1 - && matches!(assets[0], ConcreteFungible { id: Null, .. }) - && effects.len() == 2 - && matches!(effects[0], BuyExecution { .. }) - && matches!(effects[1], InitiateTeleport { ref assets, dest: X1(Parachain(..)), ref effects } - if assets.len() == 1 - && matches!(assets[0], All) - && effects.len() == 2 - && matches!(effects[0], BuyExecution { .. }) - && matches!(effects[1], DepositAsset { ref assets, dest: X1(AccountId32{..}) } - if assets.len() == 1 - && matches!(assets[0], All) - ) - ) - ) - } - // Nobody else is allowed to execute anything. - _ => false, - } - } -} - impl pallet_xcm::Config for Runtime { type Event = Event; type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; type XcmRouter = XcmRouter; // Anyone can execute XCM messages locally... type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; - // ...but they must match our filter, which requires them to be a simple withdraw + teleport. - type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts; + // ...but they must match our filter, which right now rejects everything. + type XcmExecuteFilter = (); type XcmExecutor = XcmExecutor; type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index cdc849a10429..1e0f16918a72 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -53,8 +53,7 @@ use runtime_parachains::scheduler as parachains_scheduler; use runtime_parachains::reward_points as parachains_reward_points; use runtime_parachains::runtime_api_impl::v1 as parachains_runtime_api_impl; -use xcm::v0::{MultiLocation::{self, Null, X1}, NetworkId, Xcm, Junction::Parachain}; -use xcm::v0::MultiAsset::{self, AllConcreteFungible}; +use xcm::v0::prelude::*; use xcm_executor::XcmExecutor; use xcm_builder::{ AccountId32Aliases, ChildParachainConvertsVia, SovereignSignedViaLocation, CurrencyAdapter as XcmCurrencyAdapter, @@ -907,8 +906,8 @@ pub type XcmRouter = ( ); parameter_types! { - pub const WestendForWestmint: (MultiAsset, MultiLocation) = - (AllConcreteFungible { id: Null }, X1(Parachain(1000))); + pub const WestendForWestmint: (MultiAssetFilter, MultiLocation) = + (Wild(AllOf(WildFungible, Concrete(WndLocation::get()))), X1(Parachain(1000))); } pub type TrustedTeleporters = ( xcm_builder::Case, @@ -946,52 +945,6 @@ pub type LocalOriginToLocation = ( SignedToAccountId32, ); -pub struct OnlyWithdrawTeleportForAccounts; -impl frame_support::traits::Contains<(MultiLocation, Xcm)> for OnlyWithdrawTeleportForAccounts { - fn contains((ref origin, ref msg): &(MultiLocation, Xcm)) -> bool { - use xcm::v0::{ - Xcm::WithdrawAsset, Order::{BuyExecution, InitiateTeleport, DepositAsset}, - MultiAsset::{All, ConcreteFungible}, Junction::AccountId32, - }; - match origin { - // Root is allowed to execute anything. - Null => true, - X1(AccountId32 { .. }) => { - // An account ID trying to send a message. We ensure that it's sensible. - // This checks that it's of the form: - // WithdrawAsset { - // assets: [ ConcreteFungible { id: Null } ], - // effects: [ BuyExecution, InitiateTeleport { - // assets: All, - // dest: Parachain, - // effects: [ BuyExecution, DepositAssets { - // assets: All, - // dest: AccountId32, - // } ] - // } ] - // } - matches!(msg, WithdrawAsset { ref assets, ref effects } - if assets.len() == 1 - && matches!(assets[0], ConcreteFungible { id: Null, .. }) - && effects.len() == 2 - && matches!(effects[0], BuyExecution { .. }) - && matches!(effects[1], InitiateTeleport { ref assets, dest: X1(Parachain(..)), ref effects } - if assets.len() == 1 - && matches!(assets[0], All) - && effects.len() == 2 - && matches!(effects[0], BuyExecution { .. }) - && matches!(effects[1], DepositAsset { ref assets, dest: X1(AccountId32{..}) } - if assets.len() == 1 - && matches!(assets[0], All) - ) - ) - ) - } - // Nobody else is allowed to execute anything. - _ => false, - } - } -} impl pallet_xcm::Config for Runtime { type Event = Event; @@ -999,8 +952,8 @@ impl pallet_xcm::Config for Runtime { type XcmRouter = XcmRouter; // Anyone can execute XCM messages locally... type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; - // ...but they must match our filter, which requires them to be a simple withdraw + teleport. - type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts; + // ...but they must match our filter, which rejects everything. + type XcmExecuteFilter = (); type XcmExecutor = XcmExecutor; type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index a99d630fefe3..77c004b58844 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -123,7 +123,7 @@ pub mod pallet { let mut message = Xcm::WithdrawAsset { assets: assets.clone(), effects: sp_std::vec![ InitiateTeleport { - assets: sp_std::vec![ All ], + assets: Wild(All), dest: dest.clone(), effects: sp_std::vec![], } ] @@ -134,18 +134,19 @@ pub mod pallet { origin: OriginFor, dest: MultiLocation, beneficiary: MultiLocation, - assets: Vec, + assets: MultiAssets, dest_weight: Weight, ) -> DispatchResult { let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; - let value = (origin_location, assets); + let value = (origin_location, assets.drain()); ensure!(T::XcmTeleportFilter::contains(&value), Error::::Filtered); let (origin_location, assets) = value; + let assets = assets.into(); let mut message = Xcm::WithdrawAsset { assets, effects: vec![ InitiateTeleport { - assets: vec![ All ], + assets: Wild(All), dest, effects: vec![ BuyExecution { @@ -156,7 +157,7 @@ pub mod pallet { halt_on_error: false, xcm: vec![], }, - DepositAsset { assets: vec![ All ], dest: beneficiary }, + DepositAsset { assets: Wild(All), dest: beneficiary }, ], }, ], @@ -179,7 +180,7 @@ pub mod pallet { /// - `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the /// `dest` side. /// - `dest_weight`: Equal to the total weight on `dest` of the XCM message - /// `ReserveAssetDeposit { assets, effects: [ BuyExecution{..}, DepositAsset{..} ] }`. + /// `ReserveAssetDeposited { assets, effects: [ BuyExecution{..}, DepositAsset{..} ] }`. #[pallet::weight({ let mut message = Xcm::TransferReserveAsset { assets: assets.clone(), @@ -192,13 +193,14 @@ pub mod pallet { origin: OriginFor, dest: MultiLocation, beneficiary: MultiLocation, - assets: Vec, + assets: MultiAssets, dest_weight: Weight, ) -> DispatchResult { let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; - let value = (origin_location, assets); + let value = (origin_location, assets.drain()); ensure!(T::XcmReserveTransferFilter::contains(&value), Error::::Filtered); let (origin_location, assets) = value; + let assets = assets.into(); let mut message = Xcm::TransferReserveAsset { assets, dest, @@ -211,7 +213,7 @@ pub mod pallet { halt_on_error: false, xcm: vec![], }, - DepositAsset { assets: vec![ All ], dest: beneficiary }, + DepositAsset { assets: Wild(All), dest: beneficiary }, ], }; let weight = T::Weigher::weight(&mut message) diff --git a/xcm/src/v0/mod.rs b/xcm/src/v0/mod.rs index 2d4c192d3ec0..38d940a16a19 100644 --- a/xcm/src/v0/mod.rs +++ b/xcm/src/v0/mod.rs @@ -23,7 +23,6 @@ use parity_scale_codec::{self, Encode, Decode}; use crate::{DoubleEncoded, VersionedXcm}; mod junction; -mod multi_asset; mod multi_location; mod order; mod traits; @@ -41,7 +40,8 @@ pub use traits::{Error, Result, SendXcm, ExecuteXcm, Outcome}; pub mod prelude { pub use super::junction::{Junction::*, NetworkId, BodyId, BodyPart}; pub use super::multiasset::{ - AssetId, MultiAssets, MultiAsset, + MultiAssets, MultiAsset, + AssetId::{self, *}, AssetInstance::{self, *}, MultiAssetFilter::{self, *}, Fungibility::{self, *}, @@ -104,7 +104,7 @@ pub enum Xcm { /// orders (`effects`). /// /// - `assets`: The asset(s) to be withdrawn into holding. - /// - `effects`: The order(s) to execute on the holding account. + /// - `effects`: The order(s) to execute on the holding register. /// /// Kind: *Instruction*. /// @@ -118,7 +118,7 @@ pub enum Xcm { /// been placed into `holding`. /// /// - `assets`: The asset(s) that are minted into holding. - /// - `effects`: The order(s) to execute on the holding account. + /// - `effects`: The order(s) to execute on the holding register. /// /// Safety: `origin` must be trusted to have received and be storing `assets` such that they may later be /// withdrawn should this system send a corresponding message. @@ -127,7 +127,7 @@ pub enum Xcm { /// /// Errors: #[codec(index = 1)] - ReserveAssetDeposit { assets: MultiAssets, effects: Vec> }, + ReserveAssetDeposited { assets: MultiAssets, effects: Vec> }, /// Asset(s) (`assets`) have been destroyed on the `origin` system and equivalent assets should be /// created on this system. @@ -136,7 +136,7 @@ pub enum Xcm { /// been placed into `holding`. /// /// - `assets`: The asset(s) that are minted into holding. - /// - `effects`: The order(s) to execute on the holding account. + /// - `effects`: The order(s) to execute on the holding register. /// /// Safety: `origin` must be trusted to have irrevocably destroyed the `assets` prior as a consequence of /// sending this message. @@ -147,7 +147,7 @@ pub enum Xcm { #[codec(index = 2)] TeleportAsset { assets: MultiAssets, effects: Vec> }, - /// Indication of the contents of the holding account corresponding to the `QueryHolding` order of `query_id`. + /// Indication of the contents of the holding register corresponding to the `QueryHolding` order of `query_id`. /// /// - `query_id`: The identifier of the query that resulted in this message being sent. /// - `assets`: The message content. @@ -177,11 +177,11 @@ pub enum Xcm { /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the /// ownership of `dest` within this consensus system. /// - /// Send an onward XCM message to `dest` of `ReserveAssetDeposit` with the given `effects`. + /// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`. /// /// - `assets`: The asset(s) to be withdrawn. /// - `dest`: The new owner for the assets. - /// - `effects`: The orders that should be contained in the `ReserveAssetDeposit` which is sent onwards to + /// - `effects`: The orders that should be contained in the `ReserveAssetDeposited` which is sent onwards to /// `dest`. /// /// Safety: No concerns. @@ -294,8 +294,8 @@ impl Xcm { match xcm { WithdrawAsset { assets, effects } => WithdrawAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, - ReserveAssetDeposit { assets, effects } - => ReserveAssetDeposit { assets, effects: effects.into_iter().map(Order::into).collect() }, + ReserveAssetDeposited { assets, effects } + => ReserveAssetDeposited { assets, effects: effects.into_iter().map(Order::into).collect() }, TeleportAsset { assets, effects } => TeleportAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, QueryResponse { query_id: u64, response } diff --git a/xcm/src/v0/multi_asset.rs b/xcm/src/v0/multi_asset.rs deleted file mode 100644 index fe53f6905aa5..000000000000 --- a/xcm/src/v0/multi_asset.rs +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Substrate 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. - -// Substrate 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 Cumulus. If not, see . - -//! Cross-Consensus Message format data structures. - -use alloc::vec::Vec; - -use parity_scale_codec::{self, Encode, Decode}; -use super::{MultiLocation, AssetInstance}; - -/// A single general identifier for an asset. -/// -/// Represents both fungible and non-fungible assets. May only be used to represent a single asset class. -/// -/// Wildcards may or may not be allowed by the interpreting context. -/// -/// Assets classes may be identified in one of two ways: either an abstract identifier or a concrete identifier. -/// Implementations may support only one of these. A single asset may be referenced from multiple asset identifiers, -/// though will tend to have only a single *preferred* identifier. -/// -/// ### Abstract identifiers -/// -/// 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 of -/// the consensus system in which it is interpreted. -/// -/// 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, -/// leading to confusion. It also implies that one notional asset is accounted for locally in only one way. This may not -/// be the case, e.g. where there are multiple bridge instances each providing a bridged "BTC" token yet none being -/// fungible between the others. -/// -/// Since they are meant to be absolute and universal, a global registry is needed to ensure that name collisions do not -/// occur. -/// -/// 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. -/// -/// ### Concrete identifiers -/// -/// Concrete identifiers are *relative identifiers* that specifically identify a single asset through its location in a -/// 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 of -/// central registry. -/// -/// The limitation is that the asset identifier cannot be trivially copied between consensus systems and must instead be -/// "re-anchored" whenever being moved to a new consensus system, using the two systems' relative paths. -/// -/// Throughout XCM, messages are authored such that *when interpreted from the receiver's point of view* they will have -/// the desired meaning/effect. This means that relative paths should always by constructed to be read from the point of -/// 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. -/// -/// A concrete identifier is represented by a `MultiLocation`. If a system has an unambiguous primary asset (such as -/// Bitcoin with BTC or Ethereum with ETH), then it will conventionally be identified as the chain itself. Alternative -/// and more specific ways of referring to an asset within a system include: -/// -/// - `/PalletInstance()` for a Frame chain with a single-asset pallet instance (such as an instance of the -/// Balances pallet). -/// - `/PalletInstance()/GeneralIndex()` for a Frame chain with an indexed multi-asset pallet instance -/// (such as an instance of the Assets pallet). -/// - `/AccountId32` for an ERC-20-style single-asset smart-contract on a Frame-based contracts chain. -/// - `/AccountKey20` for an ERC-20-style single-asset smart-contract on an Ethereum-like chain. -/// -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)] -pub enum MultiAsset { - /// No assets. Rarely used. - None, - - /// All assets. Typically used for the subset of assets to be used for an `Order`, and in that context means - /// "all assets currently in holding". - All, - - /// All fungible assets. Typically used for the subset of assets to be used for an `Order`, and in that context - /// means "all fungible assets currently in holding". - AllFungible, - - /// All non-fungible assets. Typically used for the subset of assets to be used for an `Order`, and in that - /// context means "all non-fungible assets currently in holding". - AllNonFungible, - - /// All fungible assets of a given abstract asset `id`entifier. - AllAbstractFungible { id: Vec }, - - /// All non-fungible assets of a given abstract asset `class`. - AllAbstractNonFungible { class: Vec }, - - /// All fungible assets of a given concrete asset `id`entifier. - AllConcreteFungible { id: MultiLocation }, - - /// All non-fungible assets of a given concrete asset `class`. - AllConcreteNonFungible { class: MultiLocation }, - - /// Some specific `amount` of the fungible asset identified by an abstract `id`. - AbstractFungible { id: Vec, #[codec(compact)] amount: u128 }, - - /// Some specific `instance` of the non-fungible asset whose `class` is identified abstractly. - AbstractNonFungible { class: Vec, instance: AssetInstance }, - - /// Some specific `amount` of the fungible asset identified by an concrete `id`. - ConcreteFungible { id: MultiLocation, #[codec(compact)] amount: u128 }, - - /// Some specific `instance` of the non-fungible asset whose `class` is identified concretely. - ConcreteNonFungible { class: MultiLocation, instance: AssetInstance }, -} - -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 { - match self { - MultiAsset::None - | MultiAsset::AbstractFungible {..} - | MultiAsset::AbstractNonFungible {..} - | MultiAsset::ConcreteFungible {..} - | MultiAsset::ConcreteNonFungible {..} - => false, - - MultiAsset::All - | MultiAsset::AllFungible - | MultiAsset::AllNonFungible - | MultiAsset::AllAbstractFungible {..} - | MultiAsset::AllConcreteFungible {..} - | MultiAsset::AllAbstractNonFungible {..} - | MultiAsset::AllConcreteNonFungible {..} - => true, - } - } - - fn is_none(&self) -> bool { - match self { - MultiAsset::None - | MultiAsset::AbstractFungible { amount: 0, .. } - | MultiAsset::ConcreteFungible { amount: 0, .. } - => true, - - _ => false, - } - } - - fn is_fungible(&self) -> bool { - match self { - MultiAsset::All - | MultiAsset::AllFungible - | MultiAsset::AllAbstractFungible {..} - | MultiAsset::AllConcreteFungible {..} - | MultiAsset::AbstractFungible {..} - | MultiAsset::ConcreteFungible {..} - => true, - - _ => false, - } - } - - fn is_non_fungible(&self) -> bool { - match self { - MultiAsset::All - | MultiAsset::AllNonFungible - | MultiAsset::AllAbstractNonFungible {..} - | MultiAsset::AllConcreteNonFungible {..} - | MultiAsset::AbstractNonFungible {..} - | MultiAsset::ConcreteNonFungible {..} - => true, - - _ => false, - } - } - - fn is_concrete_fungible(&self, id: &MultiLocation) -> bool { - match self { - MultiAsset::AllFungible => true, - MultiAsset::AllConcreteFungible { id: i } - | MultiAsset::ConcreteFungible { id: i, .. } - => i == id, - - _ => false, - } - } - - fn is_abstract_fungible(&self, id: &[u8]) -> bool { - match self { - MultiAsset::AllFungible => true, - MultiAsset::AllAbstractFungible { id: i } - | MultiAsset::AbstractFungible { id: i, .. } - => i == id, - _ => false, - } - } - - fn is_concrete_non_fungible(&self, class: &MultiLocation) -> bool { - match self { - MultiAsset::AllNonFungible => true, - MultiAsset::AllConcreteNonFungible { class: i } - | MultiAsset::ConcreteNonFungible { class: i, .. } - => i == class, - _ => false, - } - } - - fn is_abstract_non_fungible(&self, class: &[u8]) -> bool { - match self { - MultiAsset::AllNonFungible => true, - MultiAsset::AllAbstractNonFungible { class: i } - | MultiAsset::AbstractNonFungible { class: i, .. } - => i == class, - _ => false, - } - } - - 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 { - use MultiAsset::*; - - // Inner cannot be wild - if inner.is_wildcard() { return false } - // Everything contains nothing. - if inner.is_none() { return true } - - // Everything contains anything. - if self.is_all() { return true } - // Nothing contains nothing. - if self.is_none() { return false } - - match self { - // Anything fungible contains "all fungibles" - AllFungible => inner.is_fungible(), - // Anything non-fungible contains "all non-fungibles" - AllNonFungible => inner.is_non_fungible(), - - AllConcreteFungible { id } => inner.is_concrete_fungible(id), - AllAbstractFungible { id } => inner.is_abstract_fungible(id), - AllConcreteNonFungible { class } => inner.is_concrete_non_fungible(class), - AllAbstractNonFungible { class } => inner.is_abstract_non_fungible(class), - - ConcreteFungible { id, amount } => matches!( - inner, - ConcreteFungible { id: inner_id , amount: inner_amount } if inner_id == id && amount >= inner_amount - ), - AbstractFungible { id, amount } => matches!( - inner, - AbstractFungible { id: inner_id , amount: inner_amount } if inner_id == id && amount >= inner_amount - ), - ConcreteNonFungible { .. } => self == inner, - AbstractNonFungible { .. } => self == inner, - _ => false, - } - } - - pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { - use MultiAsset::*; - match self { - AllConcreteFungible { ref mut id } - | AllConcreteNonFungible { class: ref mut id } - | ConcreteFungible { ref mut id, .. } - | ConcreteNonFungible { class: ref mut id, .. } - => id.prepend_with(prepend.clone()).map_err(|_| ()), - _ => Ok(()), - } - } -} - -#[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 } }) - ); - } -} diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index c877f71a0dc6..32b36f73a296 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -23,13 +23,10 @@ //! - `MultiAssetFilter`: A combination of `Wild` and `MultiAssets` designed for efficiently filtering an XCM holding //! account. -use core::convert::{TryFrom, TryInto}; +use core::cmp::Ordering; use alloc::{vec, vec::Vec}; use parity_scale_codec::{self as codec, Encode, Decode}; -use super::{ - MultiLocation, multi_asset::MultiAsset as OldMultiAsset, -}; -use core::cmp::Ordering; +use super::MultiLocation; /// A general identifier for an instance of a non-fungible asset class. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)] @@ -58,7 +55,7 @@ pub enum AssetInstance { } /// Classification of an asset being concrete or abstract. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)] pub enum AssetId { Concrete(MultiLocation), Abstract(Vec), @@ -98,7 +95,7 @@ impl AssetId { } /// Classification of whether an asset is fungible or not, along with an mandatory amount or instance. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)] pub enum Fungibility { Fungible(u128), NonFungible(AssetInstance), @@ -127,7 +124,7 @@ impl From for Fungibility { -#[derive(Clone, Eq, PartialEq, Debug)] +#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode)] pub struct MultiAsset { pub id: AssetId, pub fun: Fungibility, @@ -155,45 +152,6 @@ impl, B: Into> From<(A, B)> for MultiAsset { } } -impl From for OldMultiAsset { - fn from(a: MultiAsset) -> Self { - use {AssetId::*, Fungibility::*, OldMultiAsset::*}; - match (a.fun, a.id) { - (Fungible(amount), Concrete(id)) => ConcreteFungible { id, amount }, - (Fungible(amount), Abstract(id)) => AbstractFungible { id, amount }, - (NonFungible(instance), Concrete(class)) => ConcreteNonFungible { class, instance }, - (NonFungible(instance), Abstract(class)) => AbstractNonFungible { class, instance }, - } - } -} - -impl TryFrom for MultiAsset { - type Error = (); - fn try_from(a: OldMultiAsset) -> Result { - use {AssetId::*, Fungibility::*, OldMultiAsset::*}; - let (fun, id) = match a { - ConcreteFungible { id, amount } if amount > 0 => (Fungible(amount), Concrete(id)), - AbstractFungible { id, amount } if amount > 0 => (Fungible(amount), Abstract(id)), - ConcreteNonFungible { class, instance } => (NonFungible(instance), Concrete(class)), - AbstractNonFungible { class, instance } => (NonFungible(instance), Abstract(class)), - _ => return Err(()), - }; - Ok(MultiAsset { fun, id }) - } -} - -impl Encode for MultiAsset { - fn encode(&self) -> Vec { - OldMultiAsset::from(self.clone()).encode() - } -} - -impl Decode for MultiAsset { - fn decode(input: &mut I) -> Result { - OldMultiAsset::decode(input)?.try_into().map_err(|_| "Unsupported wildcard".into()) - } -} - impl MultiAsset { pub fn is_fungible(&self, maybe_id: Option) -> bool { use Fungibility::*; @@ -238,7 +196,7 @@ impl Decode for MultiAssets { if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) { Ok(b) } else { - Err("Unsupported wildcard".into()) + Err("Out of order".into()) } })?; Ok(Self(r)) @@ -251,19 +209,6 @@ impl From> for MultiAssets { } } -impl From for Vec { - fn from(a: MultiAssets) -> Self { - a.0.into_iter().map(OldMultiAsset::from).collect() - } -} - -impl TryFrom> for MultiAssets { - type Error = (); - fn try_from(a: Vec) -> Result { - a.into_iter().map(MultiAsset::try_from).collect::>().map(Self) - } -} - impl MultiAssets { /// A new (empty) value. pub fn new() -> Self { @@ -315,59 +260,23 @@ impl MultiAssets { /// Classification of whether an asset is fungible or not, along with an optional amount or instance. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)] pub enum WildFungibility { Fungible, NonFungible, } -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)] pub enum WildMultiAsset { + /// All assets in the holding register, up to usize individual assets (different instances of non-fungibles could + /// as separate assets). All, // TODO: AllOf { fun: WildFungibility, id: AssetId } + /// All assets in the holding register of a given fungibility and ID. If operating on non-fungibles, then a limit + /// is provided for the maximum amount of matching instances. AllOf(WildFungibility, AssetId), } -impl Encode for WildMultiAsset { - fn encode(&self) -> Vec { - OldMultiAsset::from(self.clone()).encode() - } -} - -impl Decode for WildMultiAsset { - fn decode(input: &mut I) -> Result { - OldMultiAsset::decode(input)?.try_into().map_err(|()| "Invalid wildcard item".into()) - } -} - -impl From for OldMultiAsset { - fn from(a: WildMultiAsset) -> Self { - use {AssetId::*, WildFungibility::*, OldMultiAsset::*, WildMultiAsset::AllOf}; - match a { - WildMultiAsset::All => All, - AllOf(Fungible, Concrete(id)) => AllConcreteFungible { id }, - AllOf(Fungible, Abstract(id)) => AllAbstractFungible { id }, - AllOf(NonFungible, Concrete(class)) => AllConcreteNonFungible { class }, - AllOf(NonFungible, Abstract(class)) => AllAbstractNonFungible { class }, - } - } -} - -impl TryFrom for WildMultiAsset { - type Error = (); - fn try_from(a: OldMultiAsset) -> Result { - use {AssetId::*, WildFungibility::*, OldMultiAsset::*, WildMultiAsset::AllOf}; - Ok(match a { - All => WildMultiAsset::All, - AllConcreteFungible { id } => AllOf(Fungible, Concrete(id)), - AllAbstractFungible { id } => AllOf(Fungible, Abstract(id)), - AllConcreteNonFungible { class } => AllOf(NonFungible, Concrete(class)), - AllAbstractNonFungible { class } => AllOf(NonFungible, Abstract(class)), - _ => return Err(()), - }) - } -} - impl WildMultiAsset { /// Returns true if `self` is a super-set of the given `inner`. /// @@ -391,15 +300,20 @@ impl WildMultiAsset { } } +impl, B: Into> From<(A, B)> for WildMultiAsset { + fn from((id, fun): (A, B)) -> WildMultiAsset { + WildMultiAsset::AllOf(fun.into(), id.into()) + } +} /// `MultiAsset` collection, either `MultiAssets` or a single wildcard. Note: vectors of wildcards /// whose encoding is supported in XCM v0 are unsupported in this implementation and will result in a decode error. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)] pub enum MultiAssetFilter { - Assets(MultiAssets), + Definite(MultiAssets), Wild(WildMultiAsset), } @@ -411,50 +325,13 @@ impl From for MultiAssetFilter { impl From for MultiAssetFilter { fn from(x: MultiAsset) -> Self { - Self::Assets(vec![x].into()) + Self::Definite(vec![x].into()) } } impl From for MultiAssetFilter { fn from(x: MultiAssets) -> Self { - Self::Assets(x) - } -} - -impl From for Vec { - fn from(a: MultiAssetFilter) -> Self { - use MultiAssetFilter::*; - match a { - Assets(assets) => assets.0.into_iter().map(OldMultiAsset::from).collect(), - Wild(wild) => vec![wild.into()], - } - } -} - -impl TryFrom> for MultiAssetFilter { - type Error = (); - fn try_from(mut old_assets: Vec) -> Result { - use MultiAssetFilter::*; - if old_assets.is_empty() { - return Ok(Assets(MultiAssets::new())) - } - if old_assets.len() == 1 && old_assets[0].is_wildcard() { - return old_assets.pop().ok_or(()).and_then(|i| Ok(Wild(i.try_into()?))) - } - - MultiAssets::try_from(old_assets).map(Self::Assets) - } -} - -impl Encode for MultiAssetFilter { - fn encode(&self) -> Vec { - Vec::::from(self.clone()).encode() - } -} - -impl Decode for MultiAssetFilter { - fn decode(input: &mut I) -> Result { - Vec::::decode(input)?.try_into().map_err(|()| "Invalid items".into()) + Self::Definite(x) } } @@ -471,7 +348,7 @@ impl MultiAssetFilter { /// Returns `true` if this definitely represents no asset. pub fn is_none(&self) -> bool { - matches!(self, MultiAssetFilter::Assets(a) if a.is_none()) + matches!(self, MultiAssetFilter::Definite(a) if a.is_none()) } /// Returns true if `self` is a super-set of the given `inner`. @@ -480,7 +357,7 @@ impl MultiAssetFilter { /// For more details, see the implementation and tests. pub fn contains(&self, inner: &MultiAsset) -> bool { match self { - MultiAssetFilter::Assets(ref assets) => assets.contains(inner), + MultiAssetFilter::Definite(ref assets) => assets.contains(inner), MultiAssetFilter::Wild(ref wild) => wild.contains(inner), } } @@ -488,7 +365,7 @@ impl MultiAssetFilter { /// Prepend a `MultiLocation` to any concrete asset components, giving it a new root location. pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { match self { - MultiAssetFilter::Assets(ref mut assets) => assets.reanchor(prepend), + MultiAssetFilter::Definite(ref mut assets) => assets.reanchor(prepend), MultiAssetFilter::Wild(ref mut wild) => wild.reanchor(prepend), } } diff --git a/xcm/src/v0/order.rs b/xcm/src/v0/order.rs index fa8aa6df3393..6ee6eb20f9bc 100644 --- a/xcm/src/v0/order.rs +++ b/xcm/src/v0/order.rs @@ -44,11 +44,11 @@ pub enum Order { /// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within /// this consensus system. /// - /// Send an onward XCM message to `dest` of `ReserveAssetDeposit` with the given `effects`. + /// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`. /// /// - `assets`: The asset(s) to remove from holding. /// - `dest`: The new owner for the assets. - /// - `effects`: The orders that should be contained in the `ReserveAssetDeposit` which is sent onwards to + /// - `effects`: The orders that should be contained in the `ReserveAssetDeposited` which is sent onwards to /// `dest`. /// /// Errors: @@ -93,7 +93,7 @@ pub enum Order { /// - `query_id`: An identifier that will be replicated into the returned XCM message. /// - `dest`: A valid destination for the returned XCM message. This may be limited to the current origin. /// - `assets`: A filter for the assets that should be reported back. The assets reported back will be, asset- - /// wise, *the lesser of this value and the holding account*. No wildcards will be used when reporting assets + /// wise, *the lesser of this value and the holding register*. No wildcards will be used when reporting assets /// back. /// /// Errors: @@ -101,7 +101,7 @@ pub enum Order { QueryHolding { #[codec(compact)] query_id: u64, dest: MultiLocation, assets: MultiAssetFilter }, /// Pay for the execution of some XCM with up to `weight` picoseconds of execution time, paying for this with - /// up to `fees` from the holding account. + /// up to `fees` from the holding register. /// /// - `fees`: The asset(s) to remove from holding to pay for fees. /// diff --git a/xcm/src/v0/traits.rs b/xcm/src/v0/traits.rs index 9a01f227e766..24bfe84db853 100644 --- a/xcm/src/v0/traits.rs +++ b/xcm/src/v0/traits.rs @@ -62,7 +62,7 @@ pub enum Error { /// Used by: /// - `Transact` TooMuchWeightRequired, - /// The fees specified by the XCM message were not found in the holding account. + /// The fees specified by the XCM message were not found in the holding register. /// /// Used by: /// - `BuyExecution` diff --git a/xcm/xcm-builder/src/barriers.rs b/xcm/xcm-builder/src/barriers.rs index ea3d80660940..0a48ad6c42e2 100644 --- a/xcm/xcm-builder/src/barriers.rs +++ b/xcm/xcm-builder/src/barriers.rs @@ -53,11 +53,11 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFro match message { Xcm::TeleportAsset { effects, .. } | Xcm::WithdrawAsset { effects, ..} - | Xcm::ReserveAssetDeposit { effects, ..} + | Xcm::ReserveAssetDeposited { effects, ..} if matches!( - effects.first(), - Some(Order::BuyExecution { debt, ..}) if *debt >= shallow_weight - ) + effects.first(), + Some(Order::BuyExecution { debt, ..}) if *debt >= shallow_weight + ) => Ok(()), _ => Err(()), } diff --git a/xcm/xcm-builder/src/filter_asset_location.rs b/xcm/xcm-builder/src/filter_asset_location.rs index e095b9dbbc73..462b9421e35a 100644 --- a/xcm/xcm-builder/src/filter_asset_location.rs +++ b/xcm/xcm-builder/src/filter_asset_location.rs @@ -17,7 +17,7 @@ //! Various implementations of `FilterAssetLocation`. use sp_std::marker::PhantomData; -use xcm::v0::{MultiAsset, MultiLocation, AssetId::Concrete}; +use xcm::v0::{MultiAsset, MultiAssetFilter, MultiLocation, AssetId::Concrete}; use frame_support::traits::Get; use xcm_executor::traits::FilterAssetLocation; @@ -31,7 +31,7 @@ impl FilterAssetLocation for NativeAsset { /// Accepts an asset if it is contained in the given `T`'s `Get` implementation. pub struct Case(PhantomData); -impl> FilterAssetLocation for Case { +impl> FilterAssetLocation for Case { fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { let (a, o) = T::get(); a.contains(asset) && &o == origin diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index 0f04b89285d1..79235427be62 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -39,7 +39,7 @@ fn basic_setup_works() { #[test] fn weigher_should_work() { - let mut message = opaque::Xcm::ReserveAssetDeposit { + let mut message = opaque::Xcm::ReserveAssetDeposited { assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], effects: vec![ Order::BuyExecution { fees: All, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, @@ -124,7 +124,7 @@ fn allow_paid_should_work() { ); assert_eq!(r, Err(())); - let mut underpaying_message = opaque::Xcm::ReserveAssetDeposit { + let mut underpaying_message = opaque::Xcm::ReserveAssetDeposited { assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], effects: vec![ Order::BuyExecution { fees: All, weight: 0, debt: 20, halt_on_error: true, xcm: vec![] }, @@ -141,7 +141,7 @@ fn allow_paid_should_work() { ); assert_eq!(r, Err(())); - let mut paying_message = opaque::Xcm::ReserveAssetDeposit { + let mut paying_message = opaque::Xcm::ReserveAssetDeposited { assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], effects: vec![ Order::BuyExecution { fees: All, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, @@ -175,7 +175,7 @@ fn paying_reserve_deposit_should_work() { WeightPrice::set((X1(Parent), 1_000_000_000_000)); let origin = X1(Parent); - let message = Xcm::::ReserveAssetDeposit { + let message = Xcm::::ReserveAssetDeposited { assets: vec![ ConcreteFungible { id: X1(Parent), amount: 100 } ], effects: vec![ Order::::BuyExecution { fees: All, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, @@ -233,7 +233,7 @@ fn reserve_transfer_should_work() { assert_eq!(assets(1002), vec![ ConcreteFungible { id: Null, amount: 100 } ]); assert_eq!(sent_xcm(), vec![( X1(Parachain(2)), - Xcm::ReserveAssetDeposit { + Xcm::ReserveAssetDeposited { assets: vec![ ConcreteFungible { id: X1(Parent), amount: 100 } ], effects: vec![ Order::DepositAsset { assets: vec![ All ], dest: three } ], }) diff --git a/xcm/xcm-builder/src/weight.rs b/xcm/xcm-builder/src/weight.rs index 77f28c73f571..ef18fcf99783 100644 --- a/xcm/xcm-builder/src/weight.rs +++ b/xcm/xcm-builder/src/weight.rs @@ -31,7 +31,7 @@ impl, C: Decode + GetDispatchInfo> WeightBounds for FixedWeigh } Xcm::RelayedFrom { ref mut message, .. } => T::get().saturating_add(Self::shallow(message.as_mut())?), Xcm::WithdrawAsset { effects, .. } - | Xcm::ReserveAssetDeposit { effects, .. } + | Xcm::ReserveAssetDeposited { effects, .. } | Xcm::TeleportAsset { effects, .. } => { let inner: Weight = effects.iter_mut() @@ -55,7 +55,7 @@ impl, C: Decode + GetDispatchInfo> WeightBounds for FixedWeigh Ok(match message { Xcm::RelayedFrom { ref mut message, .. } => Self::deep(message.as_mut())?, Xcm::WithdrawAsset { effects, .. } - | Xcm::ReserveAssetDeposit { effects, .. } + | Xcm::ReserveAssetDeposited { effects, .. } | Xcm::TeleportAsset { effects, .. } => { let mut extra = 0; diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index b25b0b3e4637..5846185189f9 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -201,7 +201,7 @@ impl Assets { } }); } - MultiAssetFilter::Assets(assets) => { + MultiAssetFilter::Definite(assets) => { if !saturate { self.ensure_contains(&assets)?; } @@ -263,15 +263,6 @@ impl Assets { } } - /// Consumes `self` and returns its original value excluding `mask` iff it contains at least `mask`, as well as - /// the assets excluded. - pub fn less(mut self, mask: MultiAssetFilter) -> Result<(Assets, Assets), Self> { - match self.try_take(mask) { - Ok(taken) => Ok((self, taken)), - Err(_) => Err(self), - } - } - /// Return the assets in `self`, but (asset-wise) of no greater value than `assets`. /// /// Result is undefined if `assets` includes elements which match to the same asset more than once. @@ -312,7 +303,7 @@ impl Assets { } }); } - MultiAssetFilter::Assets(assets) => { + MultiAssetFilter::Definite(assets) => { for asset in assets.inner().iter() { match asset { MultiAsset { fun: Fungible(ref amount), ref id } => { diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index b9ec7b4a6e92..a12d714cac6c 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -133,7 +133,7 @@ impl XcmExecutor { } Some((holding, effects)) } - (origin, Xcm::ReserveAssetDeposit { assets, effects }) => { + (origin, Xcm::ReserveAssetDeposited { assets, effects }) => { // check whether we trust origin to be our reserve location for this asset. for asset in assets.inner() { // We only trust the origin to send us assets that they identify as their @@ -145,7 +145,7 @@ impl XcmExecutor { (origin, Xcm::TransferAsset { assets, dest }) => { // Take `assets` from the origin account (on-chain) and place into dest account. for asset in assets.inner() { - Config::AssetTransactor::teleport_asset(&asset, &origin, &dest)?; + Config::AssetTransactor::beam_asset(&asset, &origin, &dest)?; } None } @@ -153,10 +153,10 @@ impl XcmExecutor { // Take `assets` from the origin account (on-chain) and place into dest account. let inv_dest = Config::LocationInverter::invert_location(&dest); for asset in assets.inner() { - Config::AssetTransactor::teleport_asset(asset, &origin, &dest)?; + Config::AssetTransactor::beam_asset(asset, &origin, &dest)?; } assets.reanchor(&inv_dest)?; - Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposit { assets, effects })?; + Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposited { assets, effects })?; None } (origin, Xcm::TeleportAsset { assets, effects }) => { @@ -256,7 +256,7 @@ impl XcmExecutor { Config::AssetTransactor::deposit_asset(&asset, &dest)?; } let assets = Self::reanchored(deposited, &dest); - Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposit { assets, effects })?; + Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposited { assets, effects })?; }, Order::InitiateReserveWithdraw { assets, reserve, effects} => { let assets = Self::reanchored(holding.saturating_take(assets), &reserve); @@ -276,7 +276,7 @@ impl XcmExecutor { Config::XcmSender::send_xcm(dest, Xcm::QueryResponse { query_id, response: Response::Assets(assets) })?; } Order::BuyExecution { fees, weight, debt, halt_on_error, xcm } => { - // pay for `weight` using up to `fees` of the holding account. + // pay for `weight` using up to `fees` of the holding register. let purchasing_weight = Weight::from(weight.checked_add(debt).ok_or(XcmError::Overflow)?); let max_fee = holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?; let unspent = trader.buy_weight(purchasing_weight, max_fee)?; diff --git a/xcm/xcm-executor/src/traits/transact_asset.rs b/xcm/xcm-executor/src/traits/transact_asset.rs index 610f98977ca7..736990775a23 100644 --- a/xcm/xcm-executor/src/traits/transact_asset.rs +++ b/xcm/xcm-executor/src/traits/transact_asset.rs @@ -85,7 +85,7 @@ pub trait TransactAsset { /// Move an `asset` `from` one location in `to` another location. /// /// Attempts to use `transfer_asset` and if not available then falls back to using a two-part withdraw/deposit. - fn teleport_asset(asset: &MultiAsset, from: &MultiLocation, to: &MultiLocation) -> Result { + fn beam_asset(asset: &MultiAsset, from: &MultiLocation, to: &MultiLocation) -> Result { match Self::transfer_asset(asset, from, to) { Err(XcmError::Unimplemented) => { let assets = Self::withdraw_asset(asset, from)?; From 958d79a2140b9b1040ba10aac85cf3c49a239438 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 1 Aug 2021 20:03:10 +0200 Subject: [PATCH 10/84] Some TODOs --- xcm/src/v0/order.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xcm/src/v0/order.rs b/xcm/src/v0/order.rs index 6ee6eb20f9bc..fbeb4a5c9608 100644 --- a/xcm/src/v0/order.rs +++ b/xcm/src/v0/order.rs @@ -39,6 +39,7 @@ pub enum Order { /// /// Errors: #[codec(index = 1)] + // TODO: https://github.com/paritytech/polkadot/issues/3547 introduce `, max_assets: u32` DepositAsset { assets: MultiAssetFilter, dest: MultiLocation }, /// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within @@ -53,6 +54,7 @@ pub enum Order { /// /// Errors: #[codec(index = 2)] + // TODO: https://github.com/paritytech/polkadot/issues/3547 introduce `, max_assets: u32` DepositReserveAsset { assets: MultiAssetFilter, dest: MultiLocation, effects: Vec> }, /// Remove the asset(s) (`give`) from holding and replace them with alternative assets. @@ -107,7 +109,7 @@ pub enum Order { /// /// Errors: #[codec(index = 7)] - BuyExecution { fees: WildMultiAsset, weight: u64, debt: u64, halt_on_error: bool, xcm: Vec> }, + BuyExecution { fees: MultiAsset, weight: u64, debt: u64, halt_on_error: bool, xcm: Vec> }, } pub mod opaque { From c65624dd592e343822dea194c7ee8a01cf49f7ec Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Sun, 1 Aug 2021 20:10:29 +0200 Subject: [PATCH 11/84] add TestSendXcm XcmRouter to track sent messages --- rustfmt.toml | 20 ------------------ xcm/xcm-executor/src/lib.rs | 1 - xcm/xcm-simulator/example/src/lib.rs | 22 +++++++------------- xcm/xcm-simulator/example/src/relay_chain.rs | 7 +++---- 4 files changed, 10 insertions(+), 40 deletions(-) delete mode 100644 rustfmt.toml diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index 15e9bdcdf10f..000000000000 --- a/rustfmt.toml +++ /dev/null @@ -1,20 +0,0 @@ -# Basic -hard_tabs = true -max_width = 100 -use_small_heuristics = "Max" -# Imports -imports_granularity = "Crate" -reorder_imports = true -# Consistency -newline_style = "Unix" -# Misc -chain_width = 80 -spaces_around_ranges = false -binop_separator = "Back" -reorder_impl_items = false -match_arm_leading_pipes = "Preserve" -match_arm_blocks = false -match_block_trailing_comma = true -trailing_comma = "Vertical" -trailing_semicolon = false -use_field_init_shorthand = true diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 2760bb23999e..8f8a5c9ee617 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -15,7 +15,6 @@ // along with Polkadot. If not, see . #![cfg_attr(not(feature = "std"), no_std)] -#![feature(or_patterns)] // revert before PRing use sp_std::{prelude::*, marker::PhantomData}; use frame_support::{ diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index bdff2de239d1..a49698e9425c 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -19,7 +19,6 @@ mod relay_chain; use polkadot_parachain::primitives::Id as ParaId; use sp_runtime::{traits::AccountIdConversion, AccountId32}; -use xcm::opaque::v0::prelude::*; use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]); @@ -105,6 +104,7 @@ mod tests { use codec::Encode; use frame_support::{assert_ok, weights::Weight}; use xcm::v0::{ + prelude::OnlyChild, Junction::{self, Parachain, Parent}, MultiAsset::*, MultiLocation::*, @@ -113,6 +113,12 @@ mod tests { }; use xcm_simulator::TestExt; + // Helper function for forming buy execution message + fn buy_execution(debt: Weight) -> Order { + use xcm::opaque::v0::prelude::*; + Order::BuyExecution { fees: All, weight: 0, debt, halt_on_error: false, xcm: vec![] } + } + #[test] fn dmp() { MockNet::reset(); @@ -223,20 +229,6 @@ mod tests { }); } - // Helper function for forming buy execution message - fn buy_execution(debt: Weight) -> Order { - use xcm::opaque::v0::prelude::*; - Order::BuyExecution { fees: All, weight: 0, debt, halt_on_error: false, xcm: vec![] } - } - - fn last_relay_chain_event() -> relay_chain::Event { - relay_chain::System::events().pop().expect("Event expected").event - } - - fn last_parachain_event() -> parachain::Event { - parachain::System::events().pop().expect("Event expected").event - } - /// Scenario: /// A parachain transfers funds on the relaychain to another parachain's account. /// diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index 43ed0d39ce9b..1de6aa1bc310 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -127,10 +127,9 @@ pub type Barrier = AllowUnpaidExecutionFrom>; thread_local! { pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); } -pub fn sent_xcm() -> Vec<(MultiLocation, Xcm)> { +pub(crate) fn sent_xcm() -> Vec<(MultiLocation, Xcm)> { SENT_XCM.with(|q| (*q.borrow()).clone()) } -/// Sender that never returns error, always sends pub struct TestSendXcm; impl SendXcm for TestSendXcm { fn send_xcm(dest: MultiLocation, msg: Xcm) -> XcmResult { @@ -142,7 +141,7 @@ impl SendXcm for TestSendXcm { pub struct XcmConfig; impl Config for XcmConfig { type Call = Call; - type XcmSender = (TestSendXcm, XcmRouter); + type XcmSender = XcmRouter; type AssetTransactor = LocalAssetTransactor; type OriginConverter = LocalOriginConverter; type IsReserve = (); @@ -159,7 +158,7 @@ pub type LocalOriginToLocation = SignedToAccountId32; - type XcmRouter = XcmRouter; + type XcmRouter = (XcmRouter, TestSendXcm); // Anyone can execute XCM messages locally... type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; type XcmExecuteFilter = All<(MultiLocation, xcm::v0::Xcm)>; From e9da5eadc50bf12c41e9be031cbea8bbb660f083 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Sun, 1 Aug 2021 20:12:26 +0200 Subject: [PATCH 12/84] fix mistake removing local env stuff --- rust-toolchain | 5 ----- rustfmt.toml | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) delete mode 100644 rust-toolchain create mode 100644 rustfmt.toml diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index ae16315aa35e..000000000000 --- a/rust-toolchain +++ /dev/null @@ -1,5 +0,0 @@ -[toolchain] -channel = "nightly-2021-02-24" -components = [ "rustfmt", "clippy" ] -targets = [ "wasm32-unknown-unknown" ] -profile = "minimal" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000000..6005fb8234cd --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,20 @@ +# Basic +hard_tabs = true +max_width = 100 +use_small_heuristics = "Max" +# Imports +imports_granularity = "Crate" +reorder_imports = true +# Consistency +newline_style = "Unix" +# Misc +chain_width = 80 +spaces_around_ranges = false +binop_separator = "Back" +reorder_impl_items = false +match_arm_leading_pipes = "Preserve" +match_arm_blocks = false +match_block_trailing_comma = true +trailing_comma = "Vertical" +trailing_semicolon = false +use_field_init_shorthand = true \ No newline at end of file From abc999fbbaca8d0c77a67aa15222ca28323d9339 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Sun, 1 Aug 2021 20:16:58 +0200 Subject: [PATCH 13/84] fix --- rustfmt.toml | 2 +- xcm/xcm-simulator/example/src/relay_chain.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rustfmt.toml b/rustfmt.toml index 6005fb8234cd..15e9bdcdf10f 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -17,4 +17,4 @@ match_arm_blocks = false match_block_trailing_comma = true trailing_comma = "Vertical" trailing_semicolon = false -use_field_init_shorthand = true \ No newline at end of file +use_field_init_shorthand = true diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index 1de6aa1bc310..1796af898759 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -161,7 +161,7 @@ impl pallet_xcm::Config for Runtime { type XcmRouter = (XcmRouter, TestSendXcm); // Anyone can execute XCM messages locally... type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; - type XcmExecuteFilter = All<(MultiLocation, xcm::v0::Xcm)>; + type XcmExecuteFilter = (); type XcmExecutor = XcmExecutor; type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; From 7d47d21bcc9f4948dc53de098fad34a15e811be0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 1 Aug 2021 20:26:39 +0200 Subject: [PATCH 14/84] Further build fixes --- runtime/kusama/src/lib.rs | 7 +++---- runtime/rococo/src/lib.rs | 2 +- xcm/pallet-xcm/src/lib.rs | 12 ++++++++---- xcm/src/v0/order.rs | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index 8b307d392843..a69e46f8ef8a 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -54,8 +54,7 @@ use runtime_parachains::scheduler as parachains_scheduler; use runtime_parachains::reward_points as parachains_reward_points; use runtime_parachains::runtime_api_impl::v1 as parachains_runtime_api_impl; -use xcm::v0::{MultiLocation::{self, Null, X1}, NetworkId, BodyId, Xcm, Junction::Parachain}; -use xcm::v0::MultiAsset::{self, AllConcreteFungible}; +use xcm::v0::prelude::*; use xcm_builder::{ AccountId32Aliases, ChildParachainConvertsVia, SovereignSignedViaLocation, CurrencyAdapter as XcmCurrencyAdapter, ChildParachainAsNative, SignedAccountId32AsNative, ChildSystemParachainAsSuperuser, LocationInverter, @@ -1260,8 +1259,8 @@ pub type XcmRouter = ( ); parameter_types! { - pub const KusamaForStatemint: (MultiAsset, MultiLocation) = - (AllConcreteFungible { id: Null }, X1(Parachain(1000))); + pub const Kusama: MultiAssetFilter = Wild(AllOf(WildFungible, Concrete(KsmLocation::get()))); + pub const KusamaForStatemint: (MultiAssetFilter, MultiLocation) = (Kusama::get(), X1(Parachain(1000))); } pub type TrustedTeleporters = ( xcm_builder::Case, diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 7000c291a1ff..9d4dbdc1c53f 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -629,7 +629,7 @@ pub type XcmRouter = ( use xcm::v0::prelude::*; parameter_types! { - pub const Rococo: MultiAssetFilter = Wild(AllOf(Concrete(RocLocation::get()), WildFungible)); + pub const Rococo: MultiAssetFilter = Wild(AllOf(WildFungible, Concrete(RocLocation::get()))); pub const RococoForTick: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(100))); pub const RococoForTrick: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(110))); pub const RococoForTrack: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(120))); diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index 77c004b58844..99341c2a0e85 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -89,6 +89,8 @@ pub mod pallet { Filtered, /// The message's weight could not be determined. UnweighableMessage, + /// The assets to be sent are empty. + Empty, } #[pallet::hooks] @@ -115,8 +117,8 @@ pub mod pallet { /// from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain. /// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be /// an `AccountId32` value. - /// - `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the - /// `dest` side. + /// - `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the + /// `dest` side. May not be empty. /// - `dest_weight`: Equal to the total weight on `dest` of the XCM message /// `Teleport { assets, effects: [ BuyExecution{..}, DepositAsset{..} ] }`. #[pallet::weight({ @@ -141,6 +143,7 @@ pub mod pallet { let value = (origin_location, assets.drain()); ensure!(T::XcmTeleportFilter::contains(&value), Error::::Filtered); let (origin_location, assets) = value; + let fees = assets.first().ok_or(Error::::Empty)?.clone(); let assets = assets.into(); let mut message = Xcm::WithdrawAsset { assets, @@ -150,7 +153,7 @@ pub mod pallet { dest, effects: vec![ BuyExecution { - fees: All, + fees, // Zero weight for additional XCM (since there are none to execute) weight: 0, debt: dest_weight, @@ -200,13 +203,14 @@ pub mod pallet { let value = (origin_location, assets.drain()); ensure!(T::XcmReserveTransferFilter::contains(&value), Error::::Filtered); let (origin_location, assets) = value; + let fees = assets.first().ok_or(Error::::Empty)?.clone(); let assets = assets.into(); let mut message = Xcm::TransferReserveAsset { assets, dest, effects: vec![ BuyExecution { - fees: All, + fees, // Zero weight for additional XCM (since there are none to execute) weight: 0, debt: dest_weight, diff --git a/xcm/src/v0/order.rs b/xcm/src/v0/order.rs index fbeb4a5c9608..6162581f7e28 100644 --- a/xcm/src/v0/order.rs +++ b/xcm/src/v0/order.rs @@ -19,7 +19,7 @@ use alloc::vec::Vec; use derivative::Derivative; use parity_scale_codec::{self, Encode, Decode}; -use super::{MultiAsset, WildMultiAsset, MultiAssetFilter, MultiLocation, Xcm}; +use super::{MultiAsset, MultiAssetFilter, MultiLocation, Xcm}; /// An instruction to be executed on some or all of the assets in holding, used by asset-related XCM messages. #[derive(Derivative, Encode, Decode)] From bb5bf44d6c89261b4e6d87603bd5c6c1dc1fff73 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 1 Aug 2021 23:47:48 +0200 Subject: [PATCH 15/84] Basic compile builds --- runtime/rococo/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 9d4dbdc1c53f..f25ce35baa84 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -87,7 +87,7 @@ pub use pallet_balances::Call as BalancesCall; use polkadot_parachain::primitives::Id as ParaId; -use xcm::v0::{Xcm, MultiLocation, NetworkId, BodyId}; +use xcm::v0::{MultiLocation, NetworkId, BodyId}; use xcm_executor::XcmExecutor; use xcm_builder::{ AccountId32Aliases, ChildParachainConvertsVia, SovereignSignedViaLocation, From acef3195286d12427d097bf6e104874ad6c51bdf Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 2 Aug 2021 00:09:41 +0200 Subject: [PATCH 16/84] First test fixed --- xcm/src/v0/multiasset.rs | 12 +++++-- xcm/xcm-executor/src/assets.rs | 34 ++++++++++--------- xcm/xcm-executor/src/traits/transact_asset.rs | 10 +++--- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index 32b36f73a296..88cfa8c75364 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -323,9 +323,15 @@ impl From for MultiAssetFilter { } } -impl From for MultiAssetFilter { - fn from(x: MultiAsset) -> Self { - Self::Definite(vec![x].into()) +impl> From for MultiAssetFilter { + fn from(x: T) -> Self { + Self::Definite(vec![x.into()].into()) + } +} + +impl From> for MultiAssetFilter { + fn from(x: Vec) -> Self { + Self::Definite(x.into()) } } diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 5846185189f9..13e7c209e9f9 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -328,30 +328,32 @@ impl Assets { #[cfg(test)] mod tests { use super::*; + use xcm::v0::prelude::*; + use MultiLocation::Null; #[allow(non_snake_case)] fn AF(id: u8, amount: u128) -> MultiAsset { - MultiAsset::AbstractFungible { id: vec![id], amount } + (vec![id], amount).into() } #[allow(non_snake_case)] fn ANF(class: u8, instance_id: u128) -> MultiAsset { - MultiAsset::AbstractNonFungible { class: vec![class], instance: AssetInstance::Index { id: instance_id } } + (vec![class], AssetInstance::Index { id: instance_id }).into() } #[allow(non_snake_case)] fn CF(amount: u128) -> MultiAsset { - MultiAsset::ConcreteFungible { id: MultiLocation::Null, amount } + (Null, amount).into() } #[allow(non_snake_case)] fn CNF(instance_id: u128) -> MultiAsset { - MultiAsset::ConcreteNonFungible { class: MultiLocation::Null, instance: AssetInstance::Index { id: instance_id } } + (Null, AssetInstance::Index { id: instance_id }).into() } fn test_assets() -> Assets { - let mut assets_vec: Vec = Vec::new(); - assets_vec.push(AF(1, 100)); - assets_vec.push(ANF(2, 200)); - assets_vec.push(CF(300)); - assets_vec.push(CNF(400)); - assets_vec.into() + let mut assets = Assets::new(); + assets.subsume(AF(1, 100)); + assets.subsume(ANF(2, 200)); + assets.subsume(CF(300)); + assets.subsume(CNF(400)); + assets } #[test] @@ -393,15 +395,15 @@ mod tests { #[test] fn min_all_and_none_works() { let assets = test_assets(); - let none = vec![MultiAsset::None]; - let all = vec![MultiAsset::All]; + let none = MultiAssets::new().into(); + let all = All.into(); - let none_min = assets.min(none.iter()); + let none_min = assets.min(&none); assert_eq!(None, none_min.assets_iter().next()); - let all_min = assets.min(all.iter()); + let all_min = assets.min(&all); assert!(all_min.assets_iter().eq(assets.assets_iter())); } - +/* #[test] fn min_all_fungible_and_all_non_fungible_works() { let assets = test_assets(); @@ -549,5 +551,5 @@ mod tests { let assets = assets1.into_assets_iter().collect::>(); assert_eq!(assets, vec![AF(1, 50), ANF(2, 200)]); - } + }*/ } diff --git a/xcm/xcm-executor/src/traits/transact_asset.rs b/xcm/xcm-executor/src/traits/transact_asset.rs index 736990775a23..d4ff17ce8496 100644 --- a/xcm/xcm-executor/src/traits/transact_asset.rs +++ b/xcm/xcm-executor/src/traits/transact_asset.rs @@ -181,6 +181,8 @@ impl TransactAsset for Tuple { #[cfg(test)] mod tests { use super::*; + use xcm::v0::prelude::*; + use MultiLocation::Null; pub struct UnimplementedTransactor; impl TransactAsset for UnimplementedTransactor {} @@ -246,27 +248,27 @@ mod tests { fn defaults_to_asset_not_found() { type MultiTransactor = (UnimplementedTransactor, NotFoundTransactor, UnimplementedTransactor); - assert_eq!(MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null), Err(XcmError::AssetNotFound)); + assert_eq!(MultiTransactor::deposit_asset(&(Null, 1).into(), &Null), Err(XcmError::AssetNotFound)); } #[test] fn unimplemented_and_not_found_continue_iteration() { type MultiTransactor = (UnimplementedTransactor, NotFoundTransactor, SuccessfulTransactor); - assert_eq!(MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null), Ok(())); + assert_eq!(MultiTransactor::deposit_asset(&(Null, 1).into(), &Null), Ok(())); } #[test] fn unexpected_error_stops_iteration() { type MultiTransactor = (OverflowTransactor, SuccessfulTransactor); - assert_eq!(MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null), Err(XcmError::Overflow)); + assert_eq!(MultiTransactor::deposit_asset(&(Null, 1).into(), &Null), Err(XcmError::Overflow)); } #[test] fn success_stops_iteration() { type MultiTransactor = (SuccessfulTransactor, OverflowTransactor); - assert_eq!(MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null), Ok(())); + assert_eq!(MultiTransactor::deposit_asset(&(Null, 1).into(), &Null), Ok(())); } } From 7d700b62fe409e19522b1546252328a444cafdf9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 2 Aug 2021 00:28:03 +0200 Subject: [PATCH 17/84] All executor tests fixed --- xcm/xcm-executor/src/assets.rs | 89 ++++++------------- xcm/xcm-executor/src/traits/transact_asset.rs | 1 - 2 files changed, 29 insertions(+), 61 deletions(-) diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 13e7c209e9f9..3064a4a7312a 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -403,31 +403,17 @@ mod tests { let all_min = assets.min(&all); assert!(all_min.assets_iter().eq(assets.assets_iter())); } -/* - #[test] - fn min_all_fungible_and_all_non_fungible_works() { - let assets = test_assets(); - let fungible = vec![MultiAsset::AllFungible]; - let non_fungible = vec![MultiAsset::AllNonFungible]; - - let fungible = assets.min(fungible.iter()); - let fungible = fungible.assets_iter().collect::>(); - assert_eq!(fungible, vec![CF(300), AF(1, 100)]); - let non_fungible = assets.min(non_fungible.iter()); - let non_fungible = non_fungible.assets_iter().collect::>(); - assert_eq!(non_fungible, vec![CNF(400), ANF(2, 200)]); - } #[test] fn min_all_abstract_works() { let assets = test_assets(); - let fungible = vec![MultiAsset::AllAbstractFungible { id: vec![1] }]; - let non_fungible = vec![MultiAsset::AllAbstractNonFungible { class: vec![2] }]; + let fungible = Wild((vec![1], WildFungible).into()); + let non_fungible = Wild((vec![2], WildNonFungible).into()); - let fungible = assets.min(fungible.iter()); + let fungible = assets.min(&fungible); let fungible = fungible.assets_iter().collect::>(); assert_eq!(fungible, vec![AF(1, 100)]); - let non_fungible = assets.min(non_fungible.iter()); + let non_fungible = assets.min(&non_fungible); let non_fungible = non_fungible.assets_iter().collect::>(); assert_eq!(non_fungible, vec![ANF(2, 200)]); } @@ -435,13 +421,13 @@ mod tests { #[test] fn min_all_concrete_works() { let assets = test_assets(); - let fungible = vec![MultiAsset::AllConcreteFungible { id: MultiLocation::Null }]; - let non_fungible = vec![MultiAsset::AllConcreteNonFungible { class: MultiLocation::Null }]; + let fungible = Wild((Null, WildFungible).into()); + let non_fungible = Wild((Null, WildNonFungible).into()); - let fungible = assets.min(fungible.iter()); + let fungible = assets.min(&fungible); let fungible = fungible.assets_iter().collect::>(); assert_eq!(fungible, vec![CF(300)]); - let non_fungible = assets.min(non_fungible.iter()); + let non_fungible = assets.min(&non_fungible); let non_fungible = non_fungible.assets_iter().collect::>(); assert_eq!(non_fungible, vec![CNF(400)]); } @@ -450,18 +436,18 @@ mod tests { fn min_basic_works() { let assets1 = test_assets(); - let mut assets2_vec: Vec = Vec::new(); + let mut assets2 = Assets::new(); // This is less than 100, so it will decrease to 50 - assets2_vec.push(AF(1, 50)); + assets2.subsume(AF(1, 50)); // This asset does not exist, so not included - assets2_vec.push(ANF(2, 400)); + assets2.subsume(ANF(2, 400)); // This is more then 300, so it should stay at 300 - assets2_vec.push(CF(600)); + assets2.subsume(CF(600)); // This asset should be included - assets2_vec.push(CNF(400)); - let assets2: Assets = assets2_vec.into(); + assets2.subsume(CNF(400)); + let assets2: MultiAssets = assets2.into(); - let assets_min = assets1.min(assets2.assets_iter()); + let assets_min = assets1.min(&assets2.into()); let assets_min = assets_min.into_assets_iter().collect::>(); assert_eq!(assets_min, vec![CF(300), AF(1, 50), CNF(400)]); } @@ -469,39 +455,21 @@ mod tests { #[test] fn saturating_take_all_and_none_works() { let mut assets = test_assets(); - let none = vec![MultiAsset::None]; - let all = vec![MultiAsset::All]; - let taken_none = assets.saturating_take(none); + let taken_none = assets.saturating_take(vec![].into()); assert_eq!(None, taken_none.assets_iter().next()); - let taken_all = assets.saturating_take(all); + let taken_all = assets.saturating_take(All.into()); // Everything taken assert_eq!(None, assets.assets_iter().next()); let all_iter = taken_all.assets_iter(); assert!(all_iter.eq(test_assets().assets_iter())); } - #[test] - fn saturating_take_all_fungible_and_all_non_fungible_works() { - let mut assets = test_assets(); - let fungible = vec![MultiAsset::AllFungible]; - let non_fungible = vec![MultiAsset::AllNonFungible]; - - let fungible = assets.saturating_take(fungible); - let fungible = fungible.assets_iter().collect::>(); - assert_eq!(fungible, vec![CF(300), AF(1, 100)]); - let non_fungible = assets.saturating_take(non_fungible); - let non_fungible = non_fungible.assets_iter().collect::>(); - assert_eq!(non_fungible, [CNF(400), ANF(2, 200)]); - // Assets completely drained - assert_eq!(None, assets.assets_iter().next()); - } - #[test] fn saturating_take_all_abstract_works() { let mut assets = test_assets(); - let fungible = vec![MultiAsset::AllAbstractFungible { id: vec![1] }]; - let non_fungible = vec![MultiAsset::AllAbstractNonFungible { class: vec![2] }]; + let fungible = Wild((vec![1], WildFungible).into()); + let non_fungible = Wild((vec![2], WildNonFungible).into()); let fungible = assets.saturating_take(fungible); let fungible = fungible.assets_iter().collect::>(); @@ -517,8 +485,8 @@ mod tests { #[test] fn saturating_take_all_concrete_works() { let mut assets = test_assets(); - let fungible = vec![MultiAsset::AllConcreteFungible { id: MultiLocation::Null }]; - let non_fungible = vec![MultiAsset::AllConcreteNonFungible { class: MultiLocation::Null }]; + let fungible = Wild((Null, WildFungible).into()); + let non_fungible = Wild((Null, WildNonFungible).into()); let fungible = assets.saturating_take(fungible); let fungible = fungible.assets_iter().collect::>(); @@ -535,21 +503,22 @@ mod tests { fn saturating_take_basic_works() { let mut assets1 = test_assets(); - let mut assets2_vec: Vec = Vec::new(); + let mut assets2 = Assets::new(); // We should take 50 - assets2_vec.push(AF(1, 50)); + assets2.subsume(AF(1, 50)); // This asset should not be taken - assets2_vec.push(ANF(2, 400)); + assets2.subsume(ANF(2, 400)); // This is more then 300, so it takes everything - assets2_vec.push(CF(600)); + assets2.subsume(CF(600)); // This asset should be taken - assets2_vec.push(CNF(400)); + assets2.subsume(CNF(400)); + let assets2: MultiAssets = assets2.into(); - let taken = assets1.saturating_take(assets2_vec); + let taken = assets1.saturating_take(assets2.into()); let taken = taken.into_assets_iter().collect::>(); assert_eq!(taken, vec![CF(300), AF(1, 50), CNF(400)]); let assets = assets1.into_assets_iter().collect::>(); assert_eq!(assets, vec![AF(1, 50), ANF(2, 200)]); - }*/ + } } diff --git a/xcm/xcm-executor/src/traits/transact_asset.rs b/xcm/xcm-executor/src/traits/transact_asset.rs index d4ff17ce8496..09cd94b19918 100644 --- a/xcm/xcm-executor/src/traits/transact_asset.rs +++ b/xcm/xcm-executor/src/traits/transact_asset.rs @@ -181,7 +181,6 @@ impl TransactAsset for Tuple { #[cfg(test)] mod tests { use super::*; - use xcm::v0::prelude::*; use MultiLocation::Null; pub struct UnimplementedTransactor; From 3e62aa35b5945c786c5256baec10b3d701f5d4eb Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 2 Aug 2021 00:32:23 +0200 Subject: [PATCH 18/84] Typo --- xcm/src/v0/multiasset.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index 88cfa8c75364..c6fb07518578 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -94,7 +94,7 @@ impl AssetId { } } -/// Classification of whether an asset is fungible or not, along with an mandatory amount or instance. +/// Classification of whether an asset is fungible or not, along with a mandatory amount or instance. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)] pub enum Fungibility { Fungible(u128), From ca477f093f6e287cf3d7a3456327be8d8c7118f0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 2 Aug 2021 01:11:56 +0200 Subject: [PATCH 19/84] Optimize subsume_assets and add test --- xcm/xcm-executor/src/assets.rs | 44 ++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 3064a4a7312a..5cb8aac02a37 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -104,12 +104,32 @@ impl Assets { } /// Mutate `self` to contain all given `assets`, saturating if necessary. - pub fn subsume_assets(&mut self, assets: Assets) { - // TODO: Could be done with a much faster btree entry merge and only sum the entries with the - // same key. - for asset in assets.into_assets_iter() { - self.subsume(asset) + pub fn subsume_assets(&mut self, mut assets: Assets) { + let mut f_iter = assets.fungible.iter_mut(); + let mut g_iter = self.fungible.iter_mut(); + if let (Some(mut f), Some(mut g)) = (f_iter.next(), g_iter.next()) { + loop { + if f.0 == g.0 { + // keys are equal. in this case, we add `swlf`'s balance for the asset onto `assets`, balance, knowing + // that the `append` operation which follows will clobber `self`'s value and only use `assets`'s. + *f.1 += *g.1; + } + if f.0 <= g.0 { + f = match f_iter.next() { + Some(x) => x, + None => break, + }; + } + if f.0 >= g.0 { + g = match g_iter.next() { + Some(x) => x, + None => break, + }; + } + } } + self.fungible.append(&mut assets.fungible); + self.non_fungible.append(&mut assets.non_fungible); } /// Mutate `self` to contain the given `asset`, saturating if necessary. @@ -356,6 +376,20 @@ mod tests { assets } + #[test] + fn subsume_assets_works() { + let t1 = test_assets(); + let mut t2 = Assets::new(); + t2.subsume(AF(1, 50)); + t2.subsume(ANF(2, 100)); + t2.subsume(CF(300)); + t2.subsume(CNF(500)); + let r1 = t1.clone().subsume_assets(t2.clone()); + let mut r2 = t1.clone(); + for a in t2.assets_iter() { r2.subsume(a) } + assert_eq!(r1, r2); + } + #[test] fn into_assets_iter_works() { let assets = test_assets(); From 3cc428fce5d734f41deb17c99bae76c51722f1c2 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 2 Aug 2021 01:30:43 +0200 Subject: [PATCH 20/84] Optimize checked_sub --- xcm/xcm-executor/src/assets.rs | 52 +++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 5cb8aac02a37..bd499bd8ec62 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -274,12 +274,29 @@ impl Assets { } /// Consumes `self` and returns its original value excluding `asset` iff it contains at least `asset`. - pub fn checked_sub(mut self, asset: MultiAsset) -> Result { - // TODO: Optimize by doing this operation directly rather than converting into a MultiAssetFilter and - // constructing the unused `_taken` return value. - match self.try_take(asset.into()) { - Ok(_taken) => Ok(self), - Err(_) => Err(self), + pub fn checked_sub(mut self, asset: MultiAsset) -> Result { + match asset.fun { + Fungible(amount) => { + let remove = if let Some(balance) = self.fungible.get_mut(&asset.id) { + if *balance >= amount { + *balance -= amount; + *balance == 0 + } else { + return Err(self) + } + } else { + return Err(self) + }; + if remove { + self.fungible.remove(&asset.id); + } + Ok(self) + } + NonFungible(instance) => if self.non_fungible.remove(&(asset.id, instance)) { + Ok(self) + } else { + Err(self) + }, } } @@ -384,12 +401,33 @@ mod tests { t2.subsume(ANF(2, 100)); t2.subsume(CF(300)); t2.subsume(CNF(500)); - let r1 = t1.clone().subsume_assets(t2.clone()); + let mut r1 = t1.clone(); + r1.subsume_assets(t2.clone()); let mut r2 = t1.clone(); for a in t2.assets_iter() { r2.subsume(a) } assert_eq!(r1, r2); } + #[test] + fn checked_sub_works() { + let t = test_assets(); + let t = t.checked_sub(AF(1, 50)).unwrap(); + let t = t.checked_sub(AF(1, 51)).unwrap_err(); + let t = t.checked_sub(AF(1, 50)).unwrap(); + let t = t.checked_sub(AF(1, 1)).unwrap_err(); + let t = t.checked_sub(CF(150)).unwrap(); + let t = t.checked_sub(CF(151)).unwrap_err(); + let t = t.checked_sub(CF(150)).unwrap(); + let t = t.checked_sub(CF(1)).unwrap_err(); + let t = t.checked_sub(ANF(2, 201)).unwrap_err(); + let t = t.checked_sub(ANF(2, 200)).unwrap(); + let t = t.checked_sub(ANF(2, 200)).unwrap_err(); + let t = t.checked_sub(CNF(401)).unwrap_err(); + let t = t.checked_sub(CNF(400)).unwrap(); + let t = t.checked_sub(CNF(400)).unwrap_err(); + assert_eq!(t, Assets::new()); + } + #[test] fn into_assets_iter_works() { let assets = test_assets(); From 099aea73570a56bb07a26a07ffd34f99dc6e534c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 2 Aug 2021 01:43:54 +0200 Subject: [PATCH 21/84] XCM Builder first test fixed --- xcm/src/v0/mod.rs | 3 ++- xcm/src/v0/multiasset.rs | 12 ++++++------ xcm/src/v0/order.rs | 4 ++-- xcm/xcm-builder/src/mock.rs | 17 +++++++---------- xcm/xcm-builder/src/tests.rs | 10 +++++----- 5 files changed, 22 insertions(+), 24 deletions(-) diff --git a/xcm/src/v0/mod.rs b/xcm/src/v0/mod.rs index 38d940a16a19..473cc9f06b7b 100644 --- a/xcm/src/v0/mod.rs +++ b/xcm/src/v0/mod.rs @@ -38,7 +38,7 @@ pub use traits::{Error, Result, SendXcm, ExecuteXcm, Outcome}; /// A prelude for importing all types typically used when interacting with XCM messages. pub mod prelude { - pub use super::junction::{Junction::*, NetworkId, BodyId, BodyPart}; + pub use super::junction::{Junction::*, NetworkId::{self, *}, BodyId, BodyPart}; pub use super::multiasset::{ MultiAssets, MultiAsset, AssetId::{self, *}, @@ -52,6 +52,7 @@ pub mod prelude { pub use super::order::Order::{self, *}; pub use super::traits::{Error as XcmError, Result as XcmResult, SendXcm, ExecuteXcm, Outcome}; pub use super::{Xcm::{self, *}, OriginKind}; + pub use super::opaque; } // TODO: #2841 #XCMENCODE Efficient encodings for MultiAssets, Vec, using initial byte values 128+ to encode diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index c6fb07518578..207b117b9ed6 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -317,15 +317,15 @@ pub enum MultiAssetFilter { Wild(WildMultiAsset), } -impl From for MultiAssetFilter { - fn from(x: WildMultiAsset) -> Self { - Self::Wild(x) +impl> From for MultiAssetFilter { + fn from(x: T) -> Self { + Self::Wild(x.into()) } } -impl> From for MultiAssetFilter { - fn from(x: T) -> Self { - Self::Definite(vec![x.into()].into()) +impl From for MultiAssetFilter { + fn from(x: MultiAsset) -> Self { + Self::Definite(vec![x].into()) } } diff --git a/xcm/src/v0/order.rs b/xcm/src/v0/order.rs index 6162581f7e28..3917e6a01116 100644 --- a/xcm/src/v0/order.rs +++ b/xcm/src/v0/order.rs @@ -29,7 +29,7 @@ use super::{MultiAsset, MultiAssetFilter, MultiLocation, Xcm}; pub enum Order { /// Do nothing. Not generally used. #[codec(index = 0)] - Null, + Noop, /// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within /// this consensus system. @@ -121,7 +121,7 @@ impl Order { pub fn from(order: Order) -> Self { use Order::*; match order { - Null => Null, + Noop => Noop, DepositAsset { assets, dest } => DepositAsset { assets, dest }, DepositReserveAsset { assets, dest, effects } diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index 6e9b7ff5a322..81f926626829 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -17,10 +17,7 @@ pub use sp_std::{fmt::Debug, marker::PhantomData, cell::RefCell}; pub use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; pub use parity_scale_codec::{Encode, Decode}; -pub use xcm::v0::{ - SendXcm, MultiLocation::*, Junction::*, MultiAsset, Xcm, Order, Result as XcmResult, Error as XcmError, - OriginKind, MultiLocation, Junction, opaque, -}; +pub use xcm::v0::prelude::*; pub use frame_support::{ ensure, parameter_types, dispatch::{Dispatchable, Parameter, Weight, DispatchError, DispatchResultWithPostInfo, DispatchInfo}, @@ -139,8 +136,8 @@ impl TransactAsset for TestAssetTransactor { ASSETS.with(|a| a.borrow_mut() .get_mut(&who) .ok_or(XcmError::NotWithdrawable)? - .try_take(what.clone()) - .map_err(|()| XcmError::NotWithdrawable) + .try_take(what.clone().into()) + .map_err(|_| XcmError::NotWithdrawable) ) } } @@ -178,14 +175,14 @@ impl ConvertOrigin for TestOriginConverter { } thread_local! { - pub static IS_RESERVE: RefCell>> = RefCell::new(BTreeMap::new()); - pub static IS_TELEPORTER: RefCell>> = RefCell::new(BTreeMap::new()); + pub static IS_RESERVE: RefCell>> = RefCell::new(BTreeMap::new()); + pub static IS_TELEPORTER: RefCell>> = RefCell::new(BTreeMap::new()); } -pub fn add_reserve(from: MultiLocation, asset: MultiAsset) { +pub fn add_reserve(from: MultiLocation, asset: MultiAssetFilter) { IS_RESERVE.with(|r| r.borrow_mut().entry(from).or_default().push(asset)); } #[allow(dead_code)] -pub fn add_teleporter(from: MultiLocation, asset: MultiAsset) { +pub fn add_teleporter(from: MultiLocation, asset: MultiAssetFilter) { IS_TELEPORTER.with(|r| r.borrow_mut().entry(from).or_default().push(asset)); } pub struct TestIsReserve; diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index 79235427be62..a0af0893d6d2 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -16,15 +16,14 @@ use super::*; use super::mock::*; -use {MultiAsset::*, Option::None}; -use xcm::v0::{Order, NetworkId::Any, Outcome, Response, ExecuteXcm}; +use xcm::v0::prelude::*; use xcm_executor::{XcmExecutor, Config, traits::*}; #[test] fn basic_setup_works() { - add_reserve(X1(Parent), AllConcreteFungible { id: X1(Parent) }); + add_reserve(X1(Parent), Wild((X1(Parent), WildFungible).into())); assert!(::IsReserve::filter_asset_location( - &ConcreteFungible { id: X1(Parent), amount: 100 }, + &(X1(Parent), 100).into(), &X1(Parent), )); @@ -36,7 +35,7 @@ fn basic_setup_works() { assert_eq!(to_account(X1(AccountIndex64{index:42, network:Any})), Ok(42)); assert_eq!(to_account(Null), Ok(3000)); } - +/* #[test] fn weigher_should_work() { let mut message = opaque::Xcm::ReserveAssetDeposited { @@ -336,3 +335,4 @@ fn prepaid_result_of_query_should_get_free_execution() { let r = XcmExecutor::::execute_xcm(origin.clone(), message.clone(), weight_limit); assert_eq!(r, Outcome::Incomplete(10, XcmError::Barrier)); } +*/ From 51584424df5ead86d25364b0e837cfb3db13deaf Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 2 Aug 2021 08:55:57 +0200 Subject: [PATCH 22/84] Fix builder tests --- xcm/src/v0/mod.rs | 2 +- xcm/src/v0/multiasset.rs | 6 +++ xcm/xcm-builder/src/lib.rs | 4 +- xcm/xcm-builder/src/mock.rs | 6 +-- xcm/xcm-builder/src/tests.rs | 77 ++++++++++++++++++---------------- xcm/xcm-builder/src/weight.rs | 65 +++++++++++++++++++++++++--- xcm/xcm-executor/src/assets.rs | 16 +++---- 7 files changed, 121 insertions(+), 55 deletions(-) diff --git a/xcm/src/v0/mod.rs b/xcm/src/v0/mod.rs index 473cc9f06b7b..95bccfacf0b5 100644 --- a/xcm/src/v0/mod.rs +++ b/xcm/src/v0/mod.rs @@ -51,7 +51,7 @@ pub mod prelude { pub use super::multi_location::MultiLocation::{self, *}; pub use super::order::Order::{self, *}; pub use super::traits::{Error as XcmError, Result as XcmResult, SendXcm, ExecuteXcm, Outcome}; - pub use super::{Xcm::{self, *}, OriginKind}; + pub use super::{Xcm::{self, *}, OriginKind, Response}; pub use super::opaque; } diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index 207b117b9ed6..f00c73d8efee 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -209,6 +209,12 @@ impl From> for MultiAssets { } } +impl> From for MultiAssets { + fn from(x: T) -> Self { + Self(vec![x.into()]) + } +} + impl MultiAssets { /// A new (empty) value. pub fn new() -> Self { diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index 534261a9998d..339a9321e1ad 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -54,7 +54,9 @@ pub use fungibles_adapter::{ }; mod weight; -pub use weight::{FixedRateOfConcreteFungible, FixedWeightBounds, UsingComponents, TakeRevenue}; +pub use weight::{FixedRateOfFungible, FixedWeightBounds, UsingComponents, TakeRevenue}; +#[allow(deprecated)] +pub use weight::FixedRateOfConcreteFungible; mod matches_fungible; pub use matches_fungible::{IsAbstract, IsConcrete}; diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index 81f926626829..d036d2997df1 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -30,7 +30,7 @@ pub use xcm_executor::{ }; pub use crate::{ TakeWeightCredit, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, FixedWeightBounds, - FixedRateOfConcreteFungible, AllowKnownQueryResponses, LocationInverter, + FixedRateOfFungible, AllowKnownQueryResponses, LocationInverter, }; pub enum TestOrigin { @@ -253,7 +253,7 @@ parameter_types! { pub static AllowUnpaidFrom: Vec = vec![]; pub static AllowPaidFrom: Vec = vec![]; // 1_000_000_000_000 => 1 unit of asset for 1 unit of Weight. - pub static WeightPrice: (MultiLocation, u128) = (Null, 1_000_000_000_000); + pub static WeightPrice: (AssetId, u128) = (Null.into(), 1_000_000_000_000); } pub type TestBarrier = ( @@ -274,6 +274,6 @@ impl Config for TestConfig { type LocationInverter = LocationInverter; type Barrier = TestBarrier; type Weigher = FixedWeightBounds; - type Trader = FixedRateOfConcreteFungible; + type Trader = FixedRateOfFungible; type ResponseHandler = TestResponseHandler; } diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index a0af0893d6d2..e23c262ea660 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -35,14 +35,14 @@ fn basic_setup_works() { assert_eq!(to_account(X1(AccountIndex64{index:42, network:Any})), Ok(42)); assert_eq!(to_account(Null), Ok(3000)); } -/* + #[test] fn weigher_should_work() { let mut message = opaque::Xcm::ReserveAssetDeposited { - assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], + assets: (X1(Parent), 100).into(), effects: vec![ - Order::BuyExecution { fees: All, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, - Order::DepositAsset { assets: vec![All], dest: Null }, + Order::BuyExecution { fees: (X1(Parent), 1).into(), weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, + Order::DepositAsset { assets: All.into(), dest: Null }, ], }.into(); assert_eq!(::Weigher::shallow(&mut message), Ok(30)); @@ -51,7 +51,7 @@ fn weigher_should_work() { #[test] fn take_weight_credit_barrier_should_work() { let mut message = opaque::Xcm::TransferAsset { - assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], + assets: (X1(Parent), 100).into(), dest: Null, }; @@ -80,7 +80,7 @@ fn take_weight_credit_barrier_should_work() { #[test] fn allow_unpaid_should_work() { let mut message = opaque::Xcm::TransferAsset { - assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], + assets: (X1(Parent), 100).into(), dest: Null, }; @@ -110,7 +110,7 @@ fn allow_paid_should_work() { AllowPaidFrom::set(vec![ X1(Parent) ]); let mut message = opaque::Xcm::TransferAsset { - assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], + assets: (X1(Parent), 100).into(), dest: Null, }; @@ -123,11 +123,12 @@ fn allow_paid_should_work() { ); assert_eq!(r, Err(())); + let fees = (X1(Parent), 1).into(); let mut underpaying_message = opaque::Xcm::ReserveAssetDeposited { - assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], + assets: (X1(Parent), 100).into(), effects: vec![ - Order::BuyExecution { fees: All, weight: 0, debt: 20, halt_on_error: true, xcm: vec![] }, - Order::DepositAsset { assets: vec![All], dest: Null }, + Order::BuyExecution { fees, weight: 0, debt: 20, halt_on_error: true, xcm: vec![] }, + Order::DepositAsset { assets: All.into(), dest: Null }, ], }; @@ -140,11 +141,12 @@ fn allow_paid_should_work() { ); assert_eq!(r, Err(())); + let fees = (X1(Parent), 1).into(); let mut paying_message = opaque::Xcm::ReserveAssetDeposited { - assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], + assets: (X1(Parent), 100).into(), effects: vec![ - Order::BuyExecution { fees: All, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, - Order::DepositAsset { assets: vec![All], dest: Null }, + Order::BuyExecution { fees, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, + Order::DepositAsset { assets: All.into(), dest: Null }, ], }; @@ -170,21 +172,22 @@ fn allow_paid_should_work() { #[test] fn paying_reserve_deposit_should_work() { AllowPaidFrom::set(vec![ X1(Parent) ]); - add_reserve(X1(Parent), AllConcreteFungible { id: X1(Parent) }); - WeightPrice::set((X1(Parent), 1_000_000_000_000)); + add_reserve(X1(Parent), (X1(Parent), WildFungible).into()); + WeightPrice::set((X1(Parent).into(), 1_000_000_000_000)); let origin = X1(Parent); + let fees = (X1(Parent), 30).into(); let message = Xcm::::ReserveAssetDeposited { - assets: vec![ ConcreteFungible { id: X1(Parent), amount: 100 } ], + assets: (X1(Parent), 100).into(), effects: vec![ - Order::::BuyExecution { fees: All, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, - Order::::DepositAsset { assets: vec![ All ], dest: Null }, + Order::::BuyExecution { fees, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, + Order::::DepositAsset { assets: All.into(), dest: Null }, ], }; let weight_limit = 50; let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); assert_eq!(r, Outcome::Complete(30)); - assert_eq!(assets(3000), vec![ ConcreteFungible { id: X1(Parent), amount: 70 } ]); + assert_eq!(assets(3000), vec![(X1(Parent), 70).into()]); } #[test] @@ -192,19 +195,19 @@ fn transfer_should_work() { // we'll let them have message execution for free. AllowUnpaidFrom::set(vec![ X1(Parachain(1)) ]); // Child parachain #1 owns 1000 tokens held by us in reserve. - add_asset(1001, ConcreteFungible { id: Null, amount: 1000 }); + add_asset(1001, (Null, 1000).into()); // They want to transfer 100 of them to their sibling parachain #2 let r = XcmExecutor::::execute_xcm( X1(Parachain(1)), Xcm::TransferAsset { - assets: vec![ ConcreteFungible { id: Null, amount: 100 } ], + assets: (Null, 100).into(), dest: X1(AccountIndex64{index:3, network:Any}), }, 50, ); assert_eq!(r, Outcome::Complete(10)); - assert_eq!(assets(3), vec![ ConcreteFungible { id: Null, amount: 100 } ]); - assert_eq!(assets(1001), vec![ ConcreteFungible { id: Null, amount: 900 } ]); + assert_eq!(assets(3), vec![(Null, 100).into()]); + assert_eq!(assets(1001), vec![(Null, 900).into()]); assert_eq!(sent_xcm(), vec![]); } @@ -212,7 +215,7 @@ fn transfer_should_work() { fn reserve_transfer_should_work() { AllowUnpaidFrom::set(vec![ X1(Parachain(1)) ]); // Child parachain #1 owns 1000 tokens held by us in reserve. - add_asset(1001, ConcreteFungible { id: Null, amount: 1000 }); + add_asset(1001, (Null, 1000).into()); // The remote account owned by gav. let three = X1(AccountIndex64{index:3, network:Any}); @@ -221,20 +224,20 @@ fn reserve_transfer_should_work() { let r = XcmExecutor::::execute_xcm( X1(Parachain(1)), Xcm::TransferReserveAsset { - assets: vec![ ConcreteFungible { id: Null, amount: 100 } ], + assets: (Null, 100).into(), dest: X1(Parachain(2)), - effects: vec![ Order::DepositAsset { assets: vec![ All ], dest: three.clone() } ], + effects: vec![ Order::DepositAsset { assets: All.into(), dest: three.clone() } ], }, 50, ); assert_eq!(r, Outcome::Complete(10)); - assert_eq!(assets(1002), vec![ ConcreteFungible { id: Null, amount: 100 } ]); + assert_eq!(assets(1002), vec![(Null, 100).into()]); assert_eq!(sent_xcm(), vec![( X1(Parachain(2)), Xcm::ReserveAssetDeposited { - assets: vec![ ConcreteFungible { id: X1(Parent), amount: 100 } ], - effects: vec![ Order::DepositAsset { assets: vec![ All ], dest: three } ], + assets: (X1(Parent), 100).into(), + effects: vec![ Order::DepositAsset { assets: All.into(), dest: three } ], }) ]); } @@ -288,14 +291,15 @@ fn transacting_should_refund_weight() { fn paid_transacting_should_refund_payment_for_unused_weight() { let one = X1(AccountIndex64{index:1, network:Any}); AllowPaidFrom::set(vec![ one.clone() ]); - add_asset(1, ConcreteFungible { id: X1(Parent), amount: 100 }); - WeightPrice::set((X1(Parent), 1_000_000_000_000)); + add_asset(1, (X1(Parent), 100).into()); + WeightPrice::set((X1(Parent).into(), 1_000_000_000_000)); let origin = one.clone(); + let fees = (X1(Parent), 100).into(); let message = Xcm::::WithdrawAsset { - assets: vec![ ConcreteFungible { id: X1(Parent), amount: 100 } ], // enough for 100 units of weight. + assets: (X1(Parent), 100).into(), // enough for 100 units of weight. effects: vec![ - Order::::BuyExecution { fees: All, weight: 70, debt: 30, halt_on_error: true, xcm: vec![ + Order::::BuyExecution { fees, weight: 70, debt: 30, halt_on_error: true, xcm: vec![ Xcm::::Transact { origin_type: OriginKind::Native, require_weight_at_most: 60, @@ -303,13 +307,13 @@ fn paid_transacting_should_refund_payment_for_unused_weight() { call: TestCall::Any(60, Some(10)).encode().into(), } ] }, - Order::::DepositAsset { assets: vec![ All ], dest: one.clone() }, + Order::::DepositAsset { assets: All.into(), dest: one.clone() }, ], }; let weight_limit = 100; let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); assert_eq!(r, Outcome::Complete(50)); - assert_eq!(assets(1), vec![ ConcreteFungible { id: X1(Parent), amount: 50 } ]); + assert_eq!(assets(1), vec![(X1(Parent), 50).into()]); } #[test] @@ -319,7 +323,7 @@ fn prepaid_result_of_query_should_get_free_execution() { // We put this in manually here, but normally this would be done at the point of crafting the message. expect_response(query_id, origin.clone()); - let the_response = Response::Assets(vec![ ConcreteFungible { id: X1(Parent), amount: 100 } ]); + let the_response = Response::Assets((X1(Parent), 100).into()); let message = Xcm::::QueryResponse { query_id, response: the_response.clone(), @@ -335,4 +339,3 @@ fn prepaid_result_of_query_should_get_free_execution() { let r = XcmExecutor::::execute_xcm(origin.clone(), message.clone(), weight_limit); assert_eq!(r, Outcome::Incomplete(10, XcmError::Barrier)); } -*/ diff --git a/xcm/xcm-builder/src/weight.rs b/xcm/xcm-builder/src/weight.rs index ef18fcf99783..500db2a393c1 100644 --- a/xcm/xcm-builder/src/weight.rs +++ b/xcm/xcm-builder/src/weight.rs @@ -16,7 +16,7 @@ use sp_std::{result::Result, marker::PhantomData, convert::TryInto}; use parity_scale_codec::Decode; -use xcm::v0::{Xcm, Order, MultiAsset, MultiLocation, Error, AssetId::Concrete}; +use xcm::v0::{Xcm, Order, MultiAsset, AssetId, MultiLocation, Error, AssetId::Concrete}; use sp_runtime::traits::{Zero, Saturating, SaturatedConversion}; use frame_support::traits::{Get, OnUnbalanced as OnUnbalancedT, tokens::currency::Currency as CurrencyT}; use frame_support::weights::{Weight, GetDispatchInfo, WeightToFeePolynomial}; @@ -92,10 +92,12 @@ impl TakeRevenue for () { /// /// The constant `Get` type parameter should be the concrete fungible ID and the amount of it required for /// one second of weight. +#[deprecated = "Use `FixedRateOfFungible` instead"] pub struct FixedRateOfConcreteFungible< T: Get<(MultiLocation, u128)>, R: TakeRevenue, >(Weight, u128, PhantomData<(T, R)>); +#[allow(deprecated)] impl, R: TakeRevenue> WeightTrader for FixedRateOfConcreteFungible { fn new() -> Self { Self(0, 0, PhantomData) } @@ -115,13 +117,62 @@ impl, R: TakeRevenue> WeightTrader for FixedRateOf let amount = units_per_second * (weight as u128) / 1_000_000_000_000u128; self.0 -= weight; self.1 = self.1.saturating_sub(amount); - Some((Concrete(id), amount).into()) + if amount > 0 { + Some((Concrete(id), amount).into()) + } else { + None + } } } - +#[allow(deprecated)] impl, R: TakeRevenue> Drop for FixedRateOfConcreteFungible { fn drop(&mut self) { - R::take_revenue((Concrete(T::get().0), self.1).into()); + if self.1 > 0 { + R::take_revenue((Concrete(T::get().0), self.1).into()); + } + } +} + +/// Simple fee calculator that requires payment in a single fungible at a fixed rate. +/// +/// The constant `Get` type parameter should be the fungible ID and the amount of it required for +/// one second of weight. +pub struct FixedRateOfFungible< + T: Get<(AssetId, u128)>, + R: TakeRevenue, +>(Weight, u128, PhantomData<(T, R)>); +impl, R: TakeRevenue> WeightTrader for FixedRateOfFungible { + fn new() -> Self { Self(0, 0, PhantomData) } + + fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { + let (id, units_per_second) = T::get(); + use frame_support::weights::constants::WEIGHT_PER_SECOND; + let amount = units_per_second * (weight as u128) / (WEIGHT_PER_SECOND as u128); + let unused = payment.checked_sub((id, amount).into()).map_err(|_| Error::TooExpensive)?; + self.0 = self.0.saturating_add(weight); + self.1 = self.1.saturating_add(amount); + Ok(unused) + } + + fn refund_weight(&mut self, weight: Weight) -> Option { + let (id, units_per_second) = T::get(); + let weight = weight.min(self.0); + let amount = units_per_second * (weight as u128) / 1_000_000_000_000u128; + self.0 -= weight; + self.1 = self.1.saturating_sub(amount); + if amount > 0 { + Some((id, amount).into()) + } else { + None + } + } +} + +impl, R: TakeRevenue> Drop for FixedRateOfFungible { + fn drop(&mut self) { + if self.1 > 0 { + R::take_revenue((T::get().0, self.1).into()); + } } } @@ -159,7 +210,11 @@ impl< self.0 -= weight; self.1 = self.1.saturating_sub(amount); let amount: u128 = amount.saturated_into(); - Some((AssetId::get(), amount).into()) + if amount > 0 { + Some((AssetId::get(), amount).into()) + } else { + None + } } } diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index bd499bd8ec62..23e576a35a9b 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -35,6 +35,14 @@ pub struct Assets { pub non_fungible: BTreeSet<(AssetId, AssetInstance)>, } +impl From for Assets { + fn from(asset: MultiAsset) -> Assets { + let mut result = Self::default(); + result.subsume(asset); + result + } +} + impl From> for Assets { fn from(assets: Vec) -> Assets { let mut result = Self::default(); @@ -63,14 +71,6 @@ impl From for MultiAssets { } } -impl From for Assets { - fn from(asset: MultiAsset) -> Assets { - let mut result = Self::default(); - result.subsume(asset); - result - } -} - /// An error emitted by `take` operations. #[derive(Debug)] pub enum TakeError { From 8b5f212492f24667ab126c6c9cb594d14e8bb4b1 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 2 Aug 2021 10:57:28 +0200 Subject: [PATCH 23/84] add ReceivedDmp storage value to MockMsgQueue pallet to store all received messages for testing purposes and to test QueryHolding as a success response message --- xcm/xcm-simulator/example/src/lib.rs | 43 +++++++++++++++++--- xcm/xcm-simulator/example/src/parachain.rs | 9 +++- xcm/xcm-simulator/example/src/relay_chain.rs | 22 +--------- 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index a49698e9425c..53fe7946a5fc 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -221,7 +221,33 @@ mod tests { }); ParaA::execute_with(|| { - // free execution, full amount received + use xcm::opaque::v0::NetworkId; + use xcm_simulator::{BuyExecution, DepositAsset}; + // check message received + let expected_message = ( + X1(Parent), + ReserveAssetDeposit { + assets: vec![ConcreteFungible { id: X1(Parent), amount: 123 }], + effects: vec![ + BuyExecution { + fees: All, + weight: 0, + debt: 123, + halt_on_error: false, + xcm: vec![], + }, + DepositAsset { + assets: vec![All], + dest: X1(Junction::AccountId32 { + network: NetworkId::Any, + id: ALICE.into(), + }), + }, + ], + }, + ); + assert_eq!(parachain::MsgQueue::received_dmp(), vec![expected_message]); + // check message execution with full amount received assert_eq!( pallet_balances::Pallet::::free_balance(&ALICE), INITIAL_BALANCE + 123 @@ -286,7 +312,7 @@ mod tests { assets: vec![All], dest: OnlyChild.into(), // invalid destination }, - // is not triggered becasue the deposit fails + // is not triggered because the deposit fails Order::QueryHolding { query_id, dest: Parachain(2).into(), assets: vec![All] }, ], }; @@ -306,7 +332,6 @@ mod tests { // but deposit did not execute let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); assert_eq!(relay_chain::Balances::free_balance(para_account_b), 0); - assert_eq!(relay_chain::sent_xcm(), vec![]); }); // Now send a message which fully succeeds on the relay chain @@ -329,7 +354,7 @@ mod tests { assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); }); - // Check that transfer was executed and response message was sent + // Check that transfer was executed Relay::execute_with(|| { let spent = 20; let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); @@ -341,7 +366,15 @@ mod tests { // and deposit did execute let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); assert_eq!(relay_chain::Balances::free_balance(para_account_b), 10); - assert_eq!(relay_chain::sent_xcm(), vec![]); + }); + + // Check that QueryResponse message was received + ParaB::execute_with(|| { + use xcm::opaque::v0::Response::Assets; + assert_eq!( + parachain::MsgQueue::received_dmp(), + vec![(X1(Parent), QueryResponse { query_id: 1234, response: Assets(vec![]) })] + ); }); } } diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index f4ad471ff697..854bf1906eeb 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -175,6 +175,12 @@ pub mod mock_msg_queue { #[pallet::getter(fn parachain_id)] pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn received_dmp)] + /// A queue of received DMP messages + pub(super) type ReceivedDmp = + StorageValue<_, Vec<(MultiLocation, Xcm)>, ValueQuery>; + impl Get for Pallet { fn get() -> ParaId { Self::parachain_id() @@ -275,7 +281,8 @@ pub mod mock_msg_queue { Self::deposit_event(Event::UnsupportedVersion(id)); }, Ok(Ok(x)) => { - let outcome = T::XcmExecutor::execute_xcm(Parent.into(), x, limit); + let outcome = T::XcmExecutor::execute_xcm(Parent.into(), x.clone(), limit); + >::mutate(|y| y.push((Parent.into(), x))); Self::deposit_event(Event::ExecutedDownward(id, outcome)); }, } diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index 1796af898759..c69f20d05eaf 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -26,11 +26,7 @@ use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; use polkadot_parachain::primitives::Id as ParaId; use polkadot_runtime_parachains::{configuration, origin, shared, ump}; -use sp_std::cell::RefCell; -use xcm::{ - opaque::v0::{Result as XcmResult, SendXcm, Xcm}, - v0::{MultiAsset, MultiLocation, NetworkId}, -}; +use xcm::v0::{MultiAsset, MultiLocation, NetworkId}; use xcm_builder::{ AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, @@ -124,20 +120,6 @@ parameter_types! { pub type XcmRouter = super::RelayChainXcmRouter; pub type Barrier = AllowUnpaidExecutionFrom>; -thread_local! { - pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); -} -pub(crate) fn sent_xcm() -> Vec<(MultiLocation, Xcm)> { - SENT_XCM.with(|q| (*q.borrow()).clone()) -} -pub struct TestSendXcm; -impl SendXcm for TestSendXcm { - fn send_xcm(dest: MultiLocation, msg: Xcm) -> XcmResult { - SENT_XCM.with(|q| q.borrow_mut().push((dest, msg))); - Ok(()) - } -} - pub struct XcmConfig; impl Config for XcmConfig { type Call = Call; @@ -158,7 +140,7 @@ pub type LocalOriginToLocation = SignedToAccountId32; - type XcmRouter = (XcmRouter, TestSendXcm); + type XcmRouter = XcmRouter; // Anyone can execute XCM messages locally... type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; type XcmExecuteFilter = (); From 6814aa8db13f4a64ce133fadc44811f36c7faa3b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 2 Aug 2021 11:15:53 +0200 Subject: [PATCH 24/84] Fix doc test --- xcm/xcm-executor/src/assets.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 23e576a35a9b..41fafbf67c40 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -308,20 +308,13 @@ impl Assets { /// /// ``` /// use xcm_executor::Assets; - /// use xcm::v0::{MultiAsset, MultiLocation}; - /// let assets_i_have: Assets = vec![ - /// MultiAsset::ConcreteFungible { id: MultiLocation::Null, amount: 100 }, - /// MultiAsset::AbstractFungible { id: vec![0], amount: 100 }, - /// ].into(); - /// let assets_they_want: Assets = vec![ - /// MultiAsset::ConcreteFungible { id: MultiLocation::Null, amount: 200 }, - /// MultiAsset::AbstractFungible { id: vec![0], amount: 50 }, - /// ].into(); + /// use xcm::v0::prelude::*; + /// let assets_i_have: Assets = vec![ (Null, 100).into(), (vec![0], 100).into() ].into(); + /// let assets_they_want: MultiAssetFilter = vec![ (Null, 200).into(), (vec![0], 50).into() [.into(); /// - /// let assets_we_can_trade: Assets = assets_i_have.min(assets_they_want.assets_iter()); + /// let assets_we_can_trade: Assets = assets_i_have.min(&assets_they_want); /// assert_eq!(assets_we_can_trade.into_assets_iter().collect::>(), vec![ - /// MultiAsset::ConcreteFungible { id: MultiLocation::Null, amount: 100 }, - /// MultiAsset::AbstractFungible { id: vec![0], amount: 50 }, + /// (Null, 100).into(), (vec![0], 50).into(), /// ]); /// ``` pub fn min(&self, mask: &MultiAssetFilter) -> Assets { From 6e4e6db4cb406869296f88579b5c21a9db2c72a4 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Mon, 2 Aug 2021 13:35:11 +0200 Subject: [PATCH 25/84] improve ump docs --- runtime/parachains/src/ump.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/runtime/parachains/src/ump.rs b/runtime/parachains/src/ump.rs index c0a8af36f8a8..0b1dfb3b6679 100644 --- a/runtime/parachains/src/ump.rs +++ b/runtime/parachains/src/ump.rs @@ -42,15 +42,16 @@ pub use pallet::*; /// It is possible that by the time the message is sank the origin parachain was offboarded. It is /// up to the implementer to check that if it cares. pub trait UmpSink { - /// Process an incoming upward message and return the amount of weight it consumed, or `None` if - /// it did not begin processing a message since it would otherwise exceed `max_weight`. + /// Process an incoming upward message and return the amount of weight it consumed, or + /// `Err(message_id, required_weight)` if it did not begin processing a message since it would + /// otherwise exceed `max_weight`. /// /// See the trait docs for more details. fn process_upward_message(origin: ParaId, msg: &[u8], max_weight: Weight) -> Result; } /// An implementation of a sink that just swallows the message without consuming any weight. Returns -/// `Some(0)` indicating that no messages existed for it to process. +/// `Ok(0)` indicating that no messages existed for it to process. impl UmpSink for () { fn process_upward_message(_: ParaId, _: &[u8], _: Weight) -> Result { Ok(0) From f0e3ee3f304447834425e70e585c9de66c67468c Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Mon, 2 Aug 2021 13:47:34 +0200 Subject: [PATCH 26/84] make xcm-simulator XCM sending asynchronous --- xcm/xcm-simulator/example/src/lib.rs | 8 ++ xcm/xcm-simulator/src/lib.rs | 103 +++++++++++--- xcm/xcm-test-utils/Cargo.toml | 45 ++++++ xcm/xcm-test-utils/src/kusama_tests.rs | 100 +++++++++++++ xcm/xcm-test-utils/src/lib.rs | 24 ++++ xcm/xcm-test-utils/src/tests.rs | 187 +++++++++++++++++++++++++ 6 files changed, 448 insertions(+), 19 deletions(-) create mode 100644 xcm/xcm-test-utils/Cargo.toml create mode 100644 xcm/xcm-test-utils/src/kusama_tests.rs create mode 100644 xcm/xcm-test-utils/src/lib.rs create mode 100644 xcm/xcm-test-utils/src/tests.rs diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index f318409bf187..0e7bdb402030 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -128,6 +128,8 @@ mod tests { )); }); + RelayChainXcmRouter::process_messages().unwrap(); + ParaA::execute_with(|| { use parachain::{Event, System}; assert!(System::events() @@ -155,6 +157,8 @@ mod tests { )); }); + ParachainXcmRouter::::process_messages().unwrap(); + Relay::execute_with(|| { use relay_chain::{Event, System}; assert!(System::events() @@ -182,6 +186,8 @@ mod tests { )); }); + ParachainXcmRouter::::process_messages().unwrap(); + ParaB::execute_with(|| { use parachain::{Event, System}; assert!(System::events() @@ -204,6 +210,8 @@ mod tests { )); }); + RelayChainXcmRouter::process_messages().unwrap(); + ParaA::execute_with(|| { // free execution, full amount received assert_eq!( diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 096d62b61695..ba24dc08b483 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -16,7 +16,7 @@ //! Test kit to simulate cross-chain message passing and XCM execution -pub use codec::Encode; +pub use codec::{Decode, Encode}; pub use paste; pub use frame_support::{traits::Get, weights::Weight}; @@ -168,6 +168,18 @@ macro_rules! __impl_ext { }; } +use sp_std::collections::vec_deque::VecDeque; + +thread_local! { + pub static PARA_MESSAGE_BUS: RefCell)>> + = RefCell::new(VecDeque::new()); +} + +thread_local! { + pub static RELAY_MESSAGE_BUS: RefCell)>> + = RefCell::new(VecDeque::new()); +} + #[macro_export] macro_rules! decl_test_network { ( @@ -190,27 +202,57 @@ macro_rules! decl_test_network { /// XCM router for parachain. pub struct ParachainXcmRouter($crate::PhantomData); + impl> ParachainXcmRouter { + fn process_messages() -> $crate::XcmResult { + use $crate::{UmpSink, XcmpMessageHandlerT}; + + while let Some((para_id, destination, message)) = $crate::PARA_MESSAGE_BUS.with( + |b| b.borrow_mut().pop_front()) { + match destination { + $crate::X1($crate::Parent) => { + let encoded = $crate::encode_xcm(message, $crate::MessageKind::Ump); + let r = <$relay_chain>::process_upward_message( + para_id, &encoded[..], + $crate::Weight::max_value(), + ); + if let Err((id, required)) = r { + return Err($crate::XcmError::WeightLimitReached(required)); + } + }, + $( + $crate::X2($crate::Parent, $crate::Parachain(id)) if id == $para_id => { + let encoded = $crate::encode_xcm(message, $crate::MessageKind::Xcmp); + let messages = vec![(para_id, 1, &encoded[..])]; + let _weight = <$parachain>::handle_xcmp_messages( + messages.into_iter(), + $crate::Weight::max_value(), + ); + }, + )* + _ => { + return Err($crate::XcmError::CannotReachDestination(destination, message)); + } + } + } + + Ok(()) + } + } + impl> $crate::SendXcm for ParachainXcmRouter { fn send_xcm(destination: $crate::MultiLocation, message: $crate::Xcm<()>) -> $crate::XcmResult { use $crate::{UmpSink, XcmpMessageHandlerT}; - match destination { + match destination.clone() { $crate::X1($crate::Parent) => { - let encoded = $crate::encode_xcm(message, $crate::MessageKind::Ump); - let _ = <$relay_chain>::process_upward_message( - T::get(), &encoded[..], - $crate::Weight::max_value(), - ); + $crate::PARA_MESSAGE_BUS.with( + |b| b.borrow_mut().push_back((T::get(), destination, message))); Ok(()) }, $( $crate::X2($crate::Parent, $crate::Parachain(id)) if id == $para_id => { - let encoded = $crate::encode_xcm(message, $crate::MessageKind::Xcmp); - let messages = vec![(T::get(), 1, &encoded[..])]; - let _ = <$parachain>::handle_xcmp_messages( - messages.into_iter(), - $crate::Weight::max_value(), - ); + $crate::PARA_MESSAGE_BUS.with( + |b| b.borrow_mut().push_back((T::get(), destination, message))); Ok(()) }, )* @@ -221,18 +263,41 @@ macro_rules! decl_test_network { /// XCM router for relay chain. pub struct RelayChainXcmRouter; + + impl RelayChainXcmRouter { + fn process_messages() -> $crate::XcmResult { + use $crate::DmpMessageHandlerT; + + while let Some((destination, message)) = $crate::RELAY_MESSAGE_BUS.with( + |b| b.borrow_mut().pop_front()) { + match destination { + $( + $crate::X1($crate::Parachain(id)) if id == $para_id => { + let encoded = $crate::encode_xcm(message, $crate::MessageKind::Dmp); + // NOTE: RelayChainBlockNumber is hard-coded to 1 + let messages = vec![(1, encoded)]; + let _weight = <$parachain>::handle_dmp_messages( + messages.into_iter(), $crate::Weight::max_value(), + ); + }, + )* + _ => return Err($crate::XcmError::SendFailed("Only sends to children parachain.")), + } + } + + Ok(()) + } + } + impl $crate::SendXcm for RelayChainXcmRouter { fn send_xcm(destination: $crate::MultiLocation, message: $crate::Xcm<()>) -> $crate::XcmResult { use $crate::DmpMessageHandlerT; - match destination { + match destination.clone() { $( $crate::X1($crate::Parachain(id)) if id == $para_id => { - let encoded = $crate::encode_xcm(message, $crate::MessageKind::Dmp); - let messages = vec![(1, encoded)]; - let _ = <$parachain>::handle_dmp_messages( - messages.into_iter(), $crate::Weight::max_value(), - ); + $crate::RELAY_MESSAGE_BUS.with( + |b| b.borrow_mut().push_back((destination, message))); Ok(()) }, )* diff --git a/xcm/xcm-test-utils/Cargo.toml b/xcm/xcm-test-utils/Cargo.toml new file mode 100644 index 000000000000..bfa6c5237dde --- /dev/null +++ b/xcm/xcm-test-utils/Cargo.toml @@ -0,0 +1,45 @@ +[package] +authors = ["Parity Technologies "] +edition = "2018" +name = "xcm-test-utils" +description = "XCM testing tools" +version = "0.1.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-xcm = { path = "../pallet-xcm", default-features = false } + +polkadot-parachain = { path = "../../parachain" } +polkadot-primitives = { path = "../../primitives" } +polkadot-runtime-parachains = { path = "../../runtime/parachains" } + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +xcm = { path = "..", default-features = false } +xcm-builder = { path = "../xcm-builder", default-features = false } +xcm-executor = { path = "../xcm-executor", default-features = false } + +log = "0.4" + +[dev-dependencies] +kusama-runtime = { path = "../../runtime/kusama" } + +[features] +default = ["std"] +std = [ + "frame-support/std", + "frame-system/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", +] \ No newline at end of file diff --git a/xcm/xcm-test-utils/src/kusama_tests.rs b/xcm/xcm-test-utils/src/kusama_tests.rs new file mode 100644 index 000000000000..e2310bd70748 --- /dev/null +++ b/xcm/xcm-test-utils/src/kusama_tests.rs @@ -0,0 +1,100 @@ + +use kusama::XcmConfig; +use xcm_executor::XcmExecutor; +use MultiLocation::*; +use xcm::v0::ExecuteXcm; +use xcm::opaque::v0::Outcome; +use xcm::opaque::v0::prelude::*; +use sp_std::prelude::*; +use polkadot_primitives::v1::AccountId; +use polkadot_parachain::primitives::Id as ParaId; +use sp_runtime::traits::AccountIdConversion; + +use kusama_runtime as kusama; + +pub const ALICE: AccountId = AccountId::new([0u8; 32]); +pub const PARA_ID: u32 = 2000; +pub const INITIAL_BALANCE: u128 = 100_000_000_000; + +pub fn kusama_ext() -> sp_io::TestExternalities { + use kusama::{Runtime, System}; + + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + let parachain_acc: AccountId = ParaId::from(PARA_ID).into_account(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (ALICE, INITIAL_BALANCE), + (parachain_acc, INITIAL_BALANCE) + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + // use polkadot_primitives::v1::{MAX_CODE_SIZE, MAX_POV_SIZE}; + // // default parachains host configuration from polkadot's `chain_spec.rs` + // kusama::ParachainsConfigurationConfig { + // config: polkadot_runtime_parachains::configuration::HostConfiguration { + // validation_upgrade_frequency: 1u32, + // validation_upgrade_delay: 1, + // code_retention_period: 1200, + // max_code_size: MAX_CODE_SIZE, + // max_pov_size: MAX_POV_SIZE, + // max_head_data_size: 32 * 1024, + // group_rotation_frequency: 20, + // chain_availability_period: 4, + // thread_availability_period: 4, + // max_upward_queue_count: 8, + // max_upward_queue_size: 1024 * 1024, + // max_downward_message_size: 1024, + // // this is approximatelly 4ms. + // // + // // Same as `4 * frame_support::weights::WEIGHT_PER_MILLIS`. We don't bother with + // // an import since that's a made up number and should be replaced with a constant + // // obtained by benchmarking anyway. + // ump_service_total_weight: 4 * 1_000_000_000, + // max_upward_message_size: 1024 * 1024, + // max_upward_message_num_per_candidate: 5, + // hrmp_open_request_ttl: 5, + // hrmp_sender_deposit: 0, + // hrmp_recipient_deposit: 0, + // hrmp_channel_max_capacity: 8, + // hrmp_channel_max_total_size: 8 * 1024, + // hrmp_max_parachain_inbound_channels: 4, + // hrmp_max_parathread_inbound_channels: 4, + // hrmp_channel_max_message_size: 1024 * 1024, + // hrmp_max_parachain_outbound_channels: 4, + // hrmp_max_parathread_outbound_channels: 4, + // hrmp_max_message_num_per_candidate: 5, + // dispute_period: 6, + // no_show_slots: 2, + // n_delay_tranches: 25, + // needed_approvals: 2, + // relay_vrf_modulo_samples: 2, + // zeroth_delay_tranche_width: 0, + // ..Default::default() + // }, + // }.assimilate_storage(&mut t).unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +#[test] +fn kusama_executor_works() { + use xcm::v0::Xcm; + + kusama_ext().execute_with(|| { + let r = XcmExecutor::::execute_xcm(Parachain(PARA_ID).into(), Xcm::WithdrawAsset { + assets: vec![ConcreteFungible { id: Null, amount: 10_000_000_000 }], + effects: vec![ + Order::BuyExecution { fees: All, weight: 0, debt: 2_000_000_000, halt_on_error: false, xcm: vec![] } + ], + }, 2_000_000_000); + assert_eq!(r, Outcome::Complete(2_000_000_000)); + }) +} diff --git a/xcm/xcm-test-utils/src/lib.rs b/xcm/xcm-test-utils/src/lib.rs new file mode 100644 index 000000000000..131fc7f301ce --- /dev/null +++ b/xcm/xcm-test-utils/src/lib.rs @@ -0,0 +1,24 @@ +use xcm::opaque::v0::opaque::Xcm; +use xcm::opaque::v0::prelude::*; +use sp_std::cell::RefCell; +use sp_std::prelude::*; + +#[cfg(test)] +mod kusama_tests; + +// #[cfg(test)] +// mod tests; + +thread_local! { + pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); +} +pub fn sent_xcm() -> Vec<(MultiLocation, Xcm)> { + SENT_XCM.with(|q| (*q.borrow()).clone()) +} +pub struct MockXcmSender; +impl SendXcm for MockXcmSender { + fn send_xcm(dest: MultiLocation, msg: Xcm) -> XcmResult { + SENT_XCM.with(|q| q.borrow_mut().push((dest, msg))); + Ok(()) + } +} diff --git a/xcm/xcm-test-utils/src/tests.rs b/xcm/xcm-test-utils/src/tests.rs new file mode 100644 index 000000000000..929ebdc8dbcc --- /dev/null +++ b/xcm/xcm-test-utils/src/tests.rs @@ -0,0 +1,187 @@ +// use super::*; +use frame_support::{construct_runtime, parameter_types, traits::{All, AllowAll}, weights::Weight}; +use sp_core::H256; +use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; + +use polkadot_parachain::primitives::Id as ParaId; +use polkadot_runtime_parachains::{configuration, origin, shared, ump}; +use xcm::v0::{MultiLocation, NetworkId}; +use xcm::opaque::v0::Outcome; +use xcm::opaque::v0::MultiAsset; +use xcm_builder::{ + AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, + ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfConcreteFungible, + FixedWeightBounds, IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, +}; +use xcm_executor::XcmExecutor; + +pub type AccountId = AccountId32; +pub type Balance = u128; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl frame_system::Config for Runtime { + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = AllowAll; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type Event = Event; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; +} + +impl shared::Config for Runtime {} + +impl configuration::Config for Runtime {} + +parameter_types! { + pub const KsmLocation: MultiLocation = MultiLocation::Null; + pub const KusamaNetwork: NetworkId = NetworkId::Kusama; + pub const AnyNetwork: NetworkId = NetworkId::Any; + pub Ancestry: MultiLocation = MultiLocation::Null; + pub UnitWeightCost: Weight = 1_000; +} + +pub type SovereignAccountOf = ( + ChildParachainConvertsVia, + AccountId32Aliases, +); + +pub type LocalAssetTransactor = + XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; + +type LocalOriginConverter = ( + SovereignSignedViaLocation, + ChildParachainAsNative, + SignedAccountId32AsNative, + ChildSystemParachainAsSuperuser, +); + +parameter_types! { + pub const BaseXcmWeight: Weight = 1_000; + pub KsmPerSecond: (MultiLocation, u128) = (KsmLocation::get(), 1); +} + +pub type Barrier = AllowUnpaidExecutionFrom>; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type Call = Call; + type XcmSender = MockXcmSender; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = (); + type IsTeleporter = (); + type LocationInverter = LocationInverter; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfConcreteFungible; + type ResponseHandler = (); +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + type Event = Event; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmRouter = MockXcmSender; + // Anyone can execute XCM messages locally... + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmExecuteFilter = (); + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = All<(MultiLocation, Vec)>; + type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; + type Weigher = FixedWeightBounds; +} + +parameter_types! { + pub const FirstMessageFactorPercent: u64 = 100; +} + +impl ump::Config for Runtime { + type Event = Event; + type UmpSink = ump::XcmSink, Runtime>; + type FirstMessageFactorPercent = FirstMessageFactorPercent; +} + +impl origin::Config for Runtime {} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + ParasOrigin: origin::{Pallet, Origin}, + ParasUmp: ump::{Pallet, Call, Storage, Event}, + XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event}, + } +); + +use crate::MockXcmSender; +use crate::sent_xcm; +use MultiLocation::*; +use xcm::v0::SendXcm; +use xcm::v0::ExecuteXcm; + +#[test] +fn it_works() { + use xcm::opaque::v0::opaque::Xcm; + assert!(MockXcmSender::send_xcm(Null, Xcm::WithdrawAsset { + assets: vec![], effects: vec![] + }).is_ok()); + assert_eq!(sent_xcm(), vec![(Null, Xcm::WithdrawAsset { + assets: vec![], effects: vec![] + })]); +} + +#[test] +fn executor_works() { + use xcm::v0::Xcm; + let r = XcmExecutor::::execute_xcm(Null, Xcm::WithdrawAsset { + assets: vec![], effects: vec![] + }, 1000); + assert_eq!(r, Outcome::Complete(1000)); +} From 52bc4f04eb058e49f331d70149887f7b6ed62448 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Mon, 2 Aug 2021 13:50:01 +0200 Subject: [PATCH 27/84] remove xcm-test-utils --- xcm/xcm-test-utils/Cargo.toml | 45 ------ xcm/xcm-test-utils/src/kusama_tests.rs | 100 ------------- xcm/xcm-test-utils/src/lib.rs | 24 ---- xcm/xcm-test-utils/src/tests.rs | 187 ------------------------- 4 files changed, 356 deletions(-) delete mode 100644 xcm/xcm-test-utils/Cargo.toml delete mode 100644 xcm/xcm-test-utils/src/kusama_tests.rs delete mode 100644 xcm/xcm-test-utils/src/lib.rs delete mode 100644 xcm/xcm-test-utils/src/tests.rs diff --git a/xcm/xcm-test-utils/Cargo.toml b/xcm/xcm-test-utils/Cargo.toml deleted file mode 100644 index bfa6c5237dde..000000000000 --- a/xcm/xcm-test-utils/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[package] -authors = ["Parity Technologies "] -edition = "2018" -name = "xcm-test-utils" -description = "XCM testing tools" -version = "0.1.0" - -[dependencies] -codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } - -pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -pallet-xcm = { path = "../pallet-xcm", default-features = false } - -polkadot-parachain = { path = "../../parachain" } -polkadot-primitives = { path = "../../primitives" } -polkadot-runtime-parachains = { path = "../../runtime/parachains" } - -sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } - -xcm = { path = "..", default-features = false } -xcm-builder = { path = "../xcm-builder", default-features = false } -xcm-executor = { path = "../xcm-executor", default-features = false } - -log = "0.4" - -[dev-dependencies] -kusama-runtime = { path = "../../runtime/kusama" } - -[features] -default = ["std"] -std = [ - "frame-support/std", - "frame-system/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", - "xcm/std", - "xcm-builder/std", - "xcm-executor/std", -] \ No newline at end of file diff --git a/xcm/xcm-test-utils/src/kusama_tests.rs b/xcm/xcm-test-utils/src/kusama_tests.rs deleted file mode 100644 index e2310bd70748..000000000000 --- a/xcm/xcm-test-utils/src/kusama_tests.rs +++ /dev/null @@ -1,100 +0,0 @@ - -use kusama::XcmConfig; -use xcm_executor::XcmExecutor; -use MultiLocation::*; -use xcm::v0::ExecuteXcm; -use xcm::opaque::v0::Outcome; -use xcm::opaque::v0::prelude::*; -use sp_std::prelude::*; -use polkadot_primitives::v1::AccountId; -use polkadot_parachain::primitives::Id as ParaId; -use sp_runtime::traits::AccountIdConversion; - -use kusama_runtime as kusama; - -pub const ALICE: AccountId = AccountId::new([0u8; 32]); -pub const PARA_ID: u32 = 2000; -pub const INITIAL_BALANCE: u128 = 100_000_000_000; - -pub fn kusama_ext() -> sp_io::TestExternalities { - use kusama::{Runtime, System}; - - let mut t = frame_system::GenesisConfig::default() - .build_storage::() - .unwrap(); - - let parachain_acc: AccountId = ParaId::from(PARA_ID).into_account(); - - pallet_balances::GenesisConfig:: { - balances: vec![ - (ALICE, INITIAL_BALANCE), - (parachain_acc, INITIAL_BALANCE) - ], - } - .assimilate_storage(&mut t) - .unwrap(); - - // use polkadot_primitives::v1::{MAX_CODE_SIZE, MAX_POV_SIZE}; - // // default parachains host configuration from polkadot's `chain_spec.rs` - // kusama::ParachainsConfigurationConfig { - // config: polkadot_runtime_parachains::configuration::HostConfiguration { - // validation_upgrade_frequency: 1u32, - // validation_upgrade_delay: 1, - // code_retention_period: 1200, - // max_code_size: MAX_CODE_SIZE, - // max_pov_size: MAX_POV_SIZE, - // max_head_data_size: 32 * 1024, - // group_rotation_frequency: 20, - // chain_availability_period: 4, - // thread_availability_period: 4, - // max_upward_queue_count: 8, - // max_upward_queue_size: 1024 * 1024, - // max_downward_message_size: 1024, - // // this is approximatelly 4ms. - // // - // // Same as `4 * frame_support::weights::WEIGHT_PER_MILLIS`. We don't bother with - // // an import since that's a made up number and should be replaced with a constant - // // obtained by benchmarking anyway. - // ump_service_total_weight: 4 * 1_000_000_000, - // max_upward_message_size: 1024 * 1024, - // max_upward_message_num_per_candidate: 5, - // hrmp_open_request_ttl: 5, - // hrmp_sender_deposit: 0, - // hrmp_recipient_deposit: 0, - // hrmp_channel_max_capacity: 8, - // hrmp_channel_max_total_size: 8 * 1024, - // hrmp_max_parachain_inbound_channels: 4, - // hrmp_max_parathread_inbound_channels: 4, - // hrmp_channel_max_message_size: 1024 * 1024, - // hrmp_max_parachain_outbound_channels: 4, - // hrmp_max_parathread_outbound_channels: 4, - // hrmp_max_message_num_per_candidate: 5, - // dispute_period: 6, - // no_show_slots: 2, - // n_delay_tranches: 25, - // needed_approvals: 2, - // relay_vrf_modulo_samples: 2, - // zeroth_delay_tranche_width: 0, - // ..Default::default() - // }, - // }.assimilate_storage(&mut t).unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext -} - -#[test] -fn kusama_executor_works() { - use xcm::v0::Xcm; - - kusama_ext().execute_with(|| { - let r = XcmExecutor::::execute_xcm(Parachain(PARA_ID).into(), Xcm::WithdrawAsset { - assets: vec![ConcreteFungible { id: Null, amount: 10_000_000_000 }], - effects: vec![ - Order::BuyExecution { fees: All, weight: 0, debt: 2_000_000_000, halt_on_error: false, xcm: vec![] } - ], - }, 2_000_000_000); - assert_eq!(r, Outcome::Complete(2_000_000_000)); - }) -} diff --git a/xcm/xcm-test-utils/src/lib.rs b/xcm/xcm-test-utils/src/lib.rs deleted file mode 100644 index 131fc7f301ce..000000000000 --- a/xcm/xcm-test-utils/src/lib.rs +++ /dev/null @@ -1,24 +0,0 @@ -use xcm::opaque::v0::opaque::Xcm; -use xcm::opaque::v0::prelude::*; -use sp_std::cell::RefCell; -use sp_std::prelude::*; - -#[cfg(test)] -mod kusama_tests; - -// #[cfg(test)] -// mod tests; - -thread_local! { - pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); -} -pub fn sent_xcm() -> Vec<(MultiLocation, Xcm)> { - SENT_XCM.with(|q| (*q.borrow()).clone()) -} -pub struct MockXcmSender; -impl SendXcm for MockXcmSender { - fn send_xcm(dest: MultiLocation, msg: Xcm) -> XcmResult { - SENT_XCM.with(|q| q.borrow_mut().push((dest, msg))); - Ok(()) - } -} diff --git a/xcm/xcm-test-utils/src/tests.rs b/xcm/xcm-test-utils/src/tests.rs deleted file mode 100644 index 929ebdc8dbcc..000000000000 --- a/xcm/xcm-test-utils/src/tests.rs +++ /dev/null @@ -1,187 +0,0 @@ -// use super::*; -use frame_support::{construct_runtime, parameter_types, traits::{All, AllowAll}, weights::Weight}; -use sp_core::H256; -use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; - -use polkadot_parachain::primitives::Id as ParaId; -use polkadot_runtime_parachains::{configuration, origin, shared, ump}; -use xcm::v0::{MultiLocation, NetworkId}; -use xcm::opaque::v0::Outcome; -use xcm::opaque::v0::MultiAsset; -use xcm_builder::{ - AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, - ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfConcreteFungible, - FixedWeightBounds, IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, -}; -use xcm_executor::XcmExecutor; - -pub type AccountId = AccountId32; -pub type Balance = u128; - -parameter_types! { - pub const BlockHashCount: u64 = 250; -} - -impl frame_system::Config for Runtime { - type Origin = Origin; - type Call = Call; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Header = Header; - type Event = Event; - type BlockHashCount = BlockHashCount; - type BlockWeights = (); - type BlockLength = (); - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type DbWeight = (); - type BaseCallFilter = AllowAll; - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); -} - -parameter_types! { - pub ExistentialDeposit: Balance = 1; - pub const MaxLocks: u32 = 50; - pub const MaxReserves: u32 = 50; -} - -impl pallet_balances::Config for Runtime { - type MaxLocks = MaxLocks; - type Balance = Balance; - type Event = Event; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; -} - -impl shared::Config for Runtime {} - -impl configuration::Config for Runtime {} - -parameter_types! { - pub const KsmLocation: MultiLocation = MultiLocation::Null; - pub const KusamaNetwork: NetworkId = NetworkId::Kusama; - pub const AnyNetwork: NetworkId = NetworkId::Any; - pub Ancestry: MultiLocation = MultiLocation::Null; - pub UnitWeightCost: Weight = 1_000; -} - -pub type SovereignAccountOf = ( - ChildParachainConvertsVia, - AccountId32Aliases, -); - -pub type LocalAssetTransactor = - XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; - -type LocalOriginConverter = ( - SovereignSignedViaLocation, - ChildParachainAsNative, - SignedAccountId32AsNative, - ChildSystemParachainAsSuperuser, -); - -parameter_types! { - pub const BaseXcmWeight: Weight = 1_000; - pub KsmPerSecond: (MultiLocation, u128) = (KsmLocation::get(), 1); -} - -pub type Barrier = AllowUnpaidExecutionFrom>; - -pub struct XcmConfig; -impl xcm_executor::Config for XcmConfig { - type Call = Call; - type XcmSender = MockXcmSender; - type AssetTransactor = LocalAssetTransactor; - type OriginConverter = LocalOriginConverter; - type IsReserve = (); - type IsTeleporter = (); - type LocationInverter = LocationInverter; - type Barrier = Barrier; - type Weigher = FixedWeightBounds; - type Trader = FixedRateOfConcreteFungible; - type ResponseHandler = (); -} - -pub type LocalOriginToLocation = SignedToAccountId32; - -impl pallet_xcm::Config for Runtime { - type Event = Event; - type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; - type XcmRouter = MockXcmSender; - // Anyone can execute XCM messages locally... - type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; - type XcmExecuteFilter = (); - type XcmExecutor = XcmExecutor; - type XcmTeleportFilter = All<(MultiLocation, Vec)>; - type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; - type Weigher = FixedWeightBounds; -} - -parameter_types! { - pub const FirstMessageFactorPercent: u64 = 100; -} - -impl ump::Config for Runtime { - type Event = Event; - type UmpSink = ump::XcmSink, Runtime>; - type FirstMessageFactorPercent = FirstMessageFactorPercent; -} - -impl origin::Config for Runtime {} - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; - -construct_runtime!( - pub enum Runtime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system::{Pallet, Call, Storage, Config, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - ParasOrigin: origin::{Pallet, Origin}, - ParasUmp: ump::{Pallet, Call, Storage, Event}, - XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event}, - } -); - -use crate::MockXcmSender; -use crate::sent_xcm; -use MultiLocation::*; -use xcm::v0::SendXcm; -use xcm::v0::ExecuteXcm; - -#[test] -fn it_works() { - use xcm::opaque::v0::opaque::Xcm; - assert!(MockXcmSender::send_xcm(Null, Xcm::WithdrawAsset { - assets: vec![], effects: vec![] - }).is_ok()); - assert_eq!(sent_xcm(), vec![(Null, Xcm::WithdrawAsset { - assets: vec![], effects: vec![] - })]); -} - -#[test] -fn executor_works() { - use xcm::v0::Xcm; - let r = XcmExecutor::::execute_xcm(Null, Xcm::WithdrawAsset { - assets: vec![], effects: vec![] - }, 1000); - assert_eq!(r, Outcome::Complete(1000)); -} From 529a931a1f3058855b3ccc51274552cc4852f1cb Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 2 Aug 2021 16:12:19 +0200 Subject: [PATCH 28/84] fix some doc tests --- xcm/xcm-builder/src/matches_fungible.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/xcm/xcm-builder/src/matches_fungible.rs b/xcm/xcm-builder/src/matches_fungible.rs index 8ec1893ee9df..6b80994d1989 100644 --- a/xcm/xcm-builder/src/matches_fungible.rs +++ b/xcm/xcm-builder/src/matches_fungible.rs @@ -28,17 +28,16 @@ use xcm_executor::traits::MatchesFungible; /// # Example /// /// ``` -/// use xcm::v0::{MultiAsset, MultiLocation, Junction}; +/// use xcm::v0::prelude::*; /// use xcm_builder::IsConcrete; /// use xcm_executor::traits::MatchesFungible; /// /// frame_support::parameter_types! { -/// pub TargetLocation: MultiLocation = MultiLocation::X1(Junction::Parent); +/// pub TargetLocation: MultiLocation = X1(Parent); /// } /// /// # fn main() { -/// let id = MultiLocation::X1(Junction::Parent); -/// let asset = MultiAsset::ConcreteFungible { id, amount: 999u128 }; +/// let asset = (X1(Parent), 999).into(); /// // match `asset` if it is a concrete asset in `TargetLocation`. /// assert_eq!( as MatchesFungible>::matches_fungible(&asset), Some(999)); /// # } @@ -59,7 +58,7 @@ impl, B: TryFrom> MatchesFungible for IsConcrete< /// # Example /// /// ``` -/// use xcm::v0::{MultiAsset}; +/// use xcm::v0::prelude::*; /// use xcm_builder::IsAbstract; /// use xcm_executor::traits::MatchesFungible; /// @@ -68,7 +67,7 @@ impl, B: TryFrom> MatchesFungible for IsConcrete< /// } /// /// # fn main() { -/// let asset = MultiAsset::AbstractFungible { id: vec![7u8], amount: 999u128 }; +/// let asset = (vec![7u8], 999).into(); /// // match `asset` if it is a concrete asset in `TargetLocation`. /// assert_eq!( as MatchesFungible>::matches_fungible(&asset), Some(999)); /// # } From 3e93d54d0217b271f553d299e3314852f5c6701e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 2 Aug 2021 16:17:15 +0200 Subject: [PATCH 29/84] spelling --- scripts/gitlab/lingua.dic | 1 + xcm/src/v0/multiasset.rs | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/gitlab/lingua.dic b/scripts/gitlab/lingua.dic index a532700e3a5d..c0fc608a45b5 100644 --- a/scripts/gitlab/lingua.dic +++ b/scripts/gitlab/lingua.dic @@ -68,6 +68,7 @@ extrinsics fedora/M FRAME/MS FSMs +fungibility gameable getter/MS GiB/S diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index f00c73d8efee..9356e258d1fc 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -16,7 +16,7 @@ //! Cross-Consensus Message format asset data structures. //! -//! This encompasses four types for repesenting assets: +//! This encompasses four types for representing assets: //! - `MultiAsset`: A description of a single asset, either an instance of a non-fungible or some amount of a fungible. //! - `MultiAssets`: A collection of `MultiAsset`s. These are stored in a `Vec` and sorted with fungibles first. //! - `Wild`: A single asset wildcard, this can either be "all" assets, or all assets of a specific kind. @@ -184,7 +184,7 @@ impl MultiAsset { -/// A vec of MultiAssets. There may be no duplicate fungible items in here and when decoding, they must be sorted. +/// A `Vec` of `MultiAsset`s. There may be no duplicate fungible items in here and when decoding, they must be sorted. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode)] pub struct MultiAssets(Vec); @@ -221,7 +221,7 @@ impl MultiAssets { Self(Vec::new()) } - /// Add some asset onto the multiasset list. This is quite a laborious operation since it maintains the ordering. + /// Add some asset onto the list. This is quite a laborious operation since it maintains the ordering. pub fn push(&mut self, a: MultiAsset) { if let Fungibility::Fungible(ref amount) = a.fun { for asset in self.0.iter_mut().filter(|x| x.id == a.id) { @@ -274,7 +274,7 @@ pub enum WildFungibility { #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)] pub enum WildMultiAsset { - /// All assets in the holding register, up to usize individual assets (different instances of non-fungibles could + /// All assets in the holding register, up to `usize` individual assets (different instances of non-fungibles could /// as separate assets). All, // TODO: AllOf { fun: WildFungibility, id: AssetId } From f42b3abca40d6ada00321c21ece7793bb6d846b8 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 2 Aug 2021 16:22:32 +0200 Subject: [PATCH 30/84] named fields for AllOf --- runtime/kusama/src/lib.rs | 2 +- runtime/rococo/src/lib.rs | 2 +- runtime/westend/src/lib.rs | 2 +- xcm/src/v0/multiasset.rs | 11 +++++------ xcm/xcm-executor/src/assets.rs | 8 ++++---- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index a69e46f8ef8a..e72b7ed1b5e1 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -1259,7 +1259,7 @@ pub type XcmRouter = ( ); parameter_types! { - pub const Kusama: MultiAssetFilter = Wild(AllOf(WildFungible, Concrete(KsmLocation::get()))); + pub const Kusama: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(KsmLocation::get()) }); pub const KusamaForStatemint: (MultiAssetFilter, MultiLocation) = (Kusama::get(), X1(Parachain(1000))); } pub type TrustedTeleporters = ( diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index f25ce35baa84..292de1b633cf 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -629,7 +629,7 @@ pub type XcmRouter = ( use xcm::v0::prelude::*; parameter_types! { - pub const Rococo: MultiAssetFilter = Wild(AllOf(WildFungible, Concrete(RocLocation::get()))); + pub const Rococo: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(RocLocation::get()) }); pub const RococoForTick: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(100))); pub const RococoForTrick: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(110))); pub const RococoForTrack: (MultiAssetFilter, MultiLocation) = (Rococo::get(), X1(Parachain(120))); diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 1e0f16918a72..860b3f1bc851 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -907,7 +907,7 @@ pub type XcmRouter = ( parameter_types! { pub const WestendForWestmint: (MultiAssetFilter, MultiLocation) = - (Wild(AllOf(WildFungible, Concrete(WndLocation::get()))), X1(Parachain(1000))); + (Wild(AllOf { fun: WildFungible, id: Concrete(WndLocation::get()) }), X1(Parachain(1000))); } pub type TrustedTeleporters = ( xcm_builder::Case, diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index 9356e258d1fc..351665baaa0c 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -90,7 +90,7 @@ impl AssetId { /// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `WildMultiAsset` /// wildcard (`AllOf`) value. pub fn into_wild(self, fun: WildFungibility) -> WildMultiAsset { - WildMultiAsset::AllOf(fun, self) + WildMultiAsset::AllOf { fun, id: self } } } @@ -277,10 +277,9 @@ pub enum WildMultiAsset { /// All assets in the holding register, up to `usize` individual assets (different instances of non-fungibles could /// as separate assets). All, - // TODO: AllOf { fun: WildFungibility, id: AssetId } /// All assets in the holding register of a given fungibility and ID. If operating on non-fungibles, then a limit /// is provided for the maximum amount of matching instances. - AllOf(WildFungibility, AssetId), + AllOf { fun: WildFungibility, id: AssetId }, } impl WildMultiAsset { @@ -291,7 +290,7 @@ impl WildMultiAsset { pub fn contains(&self, inner: &MultiAsset) -> bool { use WildMultiAsset::*; match self { - AllOf(fun, id) => inner.fun.is_kind(*fun) && &inner.id == id, + AllOf { fun, id } => inner.fun.is_kind(*fun) && &inner.id == id, All => true, } } @@ -300,7 +299,7 @@ impl WildMultiAsset { pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { use WildMultiAsset::*; match self { - AllOf(_, ref mut id) => id.reanchor(prepend).map_err(|_| ()), + AllOf { ref mut id, .. } => id.reanchor(prepend).map_err(|_| ()), _ => Ok(()), } } @@ -308,7 +307,7 @@ impl WildMultiAsset { impl, B: Into> From<(A, B)> for WildMultiAsset { fn from((id, fun): (A, B)) -> WildMultiAsset { - WildMultiAsset::AllOf(fun.into(), id.into()) + WildMultiAsset::AllOf { fun: fun.into(), id: id.into() } } } diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 41fafbf67c40..85ab40a8eb9b 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -206,12 +206,12 @@ impl Assets { let mut taken = Assets::new(); match mask { MultiAssetFilter::Wild(All) => return Ok(self.swapped(Assets::new())), - MultiAssetFilter::Wild(AllOf(WildFungible, id)) => { + MultiAssetFilter::Wild(AllOf { fun: WildFungible, id }) => { if let Some((id, amount)) = self.fungible.remove_entry(&id) { taken.fungible.insert(id, amount); } } - MultiAssetFilter::Wild(AllOf(WildNonFungible, id)) => { + MultiAssetFilter::Wild(AllOf { fun: WildNonFungible, id }) => { let non_fungible = mem::replace(&mut self.non_fungible, Default::default()); non_fungible.into_iter().for_each(|(c, instance)| { if c == id { @@ -321,12 +321,12 @@ impl Assets { let mut masked = Assets::new(); match mask { MultiAssetFilter::Wild(All) => return self.clone(), - MultiAssetFilter::Wild(AllOf(WildFungible, id)) => { + MultiAssetFilter::Wild(AllOf { fun: WildFungible, id }) => { if let Some(&amount) = self.fungible.get(&id) { masked.fungible.insert(id.clone(), amount); } } - MultiAssetFilter::Wild(AllOf(WildNonFungible, id)) => { + MultiAssetFilter::Wild(AllOf { fun: WildNonFungible, id }) => { self.non_fungible.iter().for_each(|(ref c, ref instance)| { if c == id { masked.non_fungible.insert((c.clone(), instance.clone())); From bfa99132beaf86f5b717e1f6778ded37865ec078 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Mon, 2 Aug 2021 16:23:27 +0200 Subject: [PATCH 31/84] Update xcm/xcm-simulator/src/lib.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- xcm/xcm-simulator/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index ba24dc08b483..2a708cd01473 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -173,9 +173,6 @@ use sp_std::collections::vec_deque::VecDeque; thread_local! { pub static PARA_MESSAGE_BUS: RefCell)>> = RefCell::new(VecDeque::new()); -} - -thread_local! { pub static RELAY_MESSAGE_BUS: RefCell)>> = RefCell::new(VecDeque::new()); } From 86f7d510be6a40513aa86e2d17ee67f475aa8ef1 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 2 Aug 2021 18:21:06 +0200 Subject: [PATCH 32/84] use automatic dispatch of message queues to check query response functionality as success message --- xcm/xcm-simulator/example/src/lib.rs | 12 +-- xcm/xcm-simulator/src/lib.rs | 115 +++++++++++++++------------ 2 files changed, 67 insertions(+), 60 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 322215bc4047..d39d0a9ee523 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -138,8 +138,6 @@ mod tests { )); }); - RelayChainXcmRouter::process_messages().unwrap(); - ParaA::execute_with(|| { use parachain::{Event, System}; assert!(System::events() @@ -167,8 +165,6 @@ mod tests { )); }); - ParachainXcmRouter::::process_messages().unwrap(); - Relay::execute_with(|| { use relay_chain::{Event, System}; assert!(System::events() @@ -196,8 +192,6 @@ mod tests { )); }); - ParachainXcmRouter::::process_messages().unwrap(); - ParaB::execute_with(|| { use parachain::{Event, System}; assert!(System::events() @@ -226,8 +220,6 @@ mod tests { assert_eq!(parachain::Balances::free_balance(¶_account_a), INITIAL_BALANCE + 123); }); - RelayChainXcmRouter::process_messages().unwrap(); - ParaA::execute_with(|| { use xcm::opaque::v0::NetworkId; use xcm_simulator::{BuyExecution, DepositAsset}; @@ -355,7 +347,7 @@ mod tests { assets: vec![All], dest: Parachain(2).into(), // valid destination }, - Order::QueryHolding { query_id, dest: Parachain(2).into(), assets: vec![All] }, + Order::QueryHolding { query_id, dest: Parachain(1).into(), assets: vec![All] }, ], }; // Send withdraw and deposit with query holding @@ -377,7 +369,7 @@ mod tests { }); // Check that QueryResponse message was received - ParaB::execute_with(|| { + ParaA::execute_with(|| { use xcm::opaque::v0::Response::Assets; assert_eq!( parachain::MsgQueue::received_dmp(), diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 2a708cd01473..66cd704d1ffc 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -36,8 +36,16 @@ pub use xcm::{v0::prelude::*, VersionedXcm}; pub use xcm_executor::XcmExecutor; pub trait TestExt { + /// Initialize the test environment. fn new_ext() -> sp_io::TestExternalities; + /// Resets the state of the test environment. fn reset_ext(); + /// Execute some code in the context of the test externalities, with manual + /// message processing which requires calling `process_messages()`. + fn execute_without_auto_dispatch(execute: impl FnOnce() -> R) -> R; + /// Execute some code in the context of the test externalities, with + /// automatic processing of messages. + /// Messages are dispatched once the passed closure completes. fn execute_with(execute: impl FnOnce() -> R) -> R; } @@ -161,9 +169,15 @@ macro_rules! __impl_ext { $ext_name.with(|v| *v.borrow_mut() = $new_ext); } - fn execute_with(execute: impl FnOnce() -> R) -> R { + fn execute_without_auto_dispatch(execute: impl FnOnce() -> R) -> R { $ext_name.with(|v| v.borrow_mut().execute_with(execute)) } + + fn execute_with(execute: impl FnOnce() -> R) -> R { + let result = $ext_name.with(|v| v.borrow_mut().execute_with(execute)); + process_messages().expect("message processing failure"); + result + } } }; } @@ -196,44 +210,47 @@ macro_rules! decl_test_network { } } + fn process_messages() -> $crate::XcmResult { + process_relay_messages()?; + process_para_messages() + } + /// XCM router for parachain. pub struct ParachainXcmRouter($crate::PhantomData); - impl> ParachainXcmRouter { - fn process_messages() -> $crate::XcmResult { - use $crate::{UmpSink, XcmpMessageHandlerT}; + fn process_para_messages() -> $crate::XcmResult { + use $crate::{UmpSink, XcmpMessageHandlerT}; - while let Some((para_id, destination, message)) = $crate::PARA_MESSAGE_BUS.with( - |b| b.borrow_mut().pop_front()) { - match destination { - $crate::X1($crate::Parent) => { - let encoded = $crate::encode_xcm(message, $crate::MessageKind::Ump); - let r = <$relay_chain>::process_upward_message( - para_id, &encoded[..], + while let Some((para_id, destination, message)) = $crate::PARA_MESSAGE_BUS.with( + |b| b.borrow_mut().pop_front()) { + match destination { + $crate::X1($crate::Parent) => { + let encoded = $crate::encode_xcm(message, $crate::MessageKind::Ump); + let r = <$relay_chain>::process_upward_message( + para_id, &encoded[..], + $crate::Weight::max_value(), + ); + if let Err((id, required)) = r { + return Err($crate::XcmError::WeightLimitReached(required)); + } + }, + $( + $crate::X2($crate::Parent, $crate::Parachain(id)) if id == $para_id => { + let encoded = $crate::encode_xcm(message, $crate::MessageKind::Xcmp); + let messages = vec![(para_id, 1, &encoded[..])]; + let _weight = <$parachain>::handle_xcmp_messages( + messages.into_iter(), $crate::Weight::max_value(), ); - if let Err((id, required)) = r { - return Err($crate::XcmError::WeightLimitReached(required)); - } }, - $( - $crate::X2($crate::Parent, $crate::Parachain(id)) if id == $para_id => { - let encoded = $crate::encode_xcm(message, $crate::MessageKind::Xcmp); - let messages = vec![(para_id, 1, &encoded[..])]; - let _weight = <$parachain>::handle_xcmp_messages( - messages.into_iter(), - $crate::Weight::max_value(), - ); - }, - )* - _ => { - return Err($crate::XcmError::CannotReachDestination(destination, message)); - } + )* + _ => { + return Err($crate::XcmError::CannotReachDestination(destination, message)); } } - - Ok(()) } + + Ok(()) } impl> $crate::SendXcm for ParachainXcmRouter { @@ -261,29 +278,27 @@ macro_rules! decl_test_network { /// XCM router for relay chain. pub struct RelayChainXcmRouter; - impl RelayChainXcmRouter { - fn process_messages() -> $crate::XcmResult { - use $crate::DmpMessageHandlerT; + fn process_relay_messages() -> $crate::XcmResult { + use $crate::DmpMessageHandlerT; - while let Some((destination, message)) = $crate::RELAY_MESSAGE_BUS.with( - |b| b.borrow_mut().pop_front()) { - match destination { - $( - $crate::X1($crate::Parachain(id)) if id == $para_id => { - let encoded = $crate::encode_xcm(message, $crate::MessageKind::Dmp); - // NOTE: RelayChainBlockNumber is hard-coded to 1 - let messages = vec![(1, encoded)]; - let _weight = <$parachain>::handle_dmp_messages( - messages.into_iter(), $crate::Weight::max_value(), - ); - }, - )* - _ => return Err($crate::XcmError::SendFailed("Only sends to children parachain.")), - } + while let Some((destination, message)) = $crate::RELAY_MESSAGE_BUS.with( + |b| b.borrow_mut().pop_front()) { + match destination { + $( + $crate::X1($crate::Parachain(id)) if id == $para_id => { + let encoded = $crate::encode_xcm(message, $crate::MessageKind::Dmp); + // NOTE: RelayChainBlockNumber is hard-coded to 1 + let messages = vec![(1, encoded)]; + let _weight = <$parachain>::handle_dmp_messages( + messages.into_iter(), $crate::Weight::max_value(), + ); + }, + )* + _ => return Err($crate::XcmError::SendFailed("Only sends to children parachain.")), } - - Ok(()) } + + Ok(()) } impl $crate::SendXcm for RelayChainXcmRouter { @@ -303,4 +318,4 @@ macro_rules! decl_test_network { } } }; -} +} \ No newline at end of file From d2b81f96488a97e76e14a94ded86aae2a063a20c Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 2 Aug 2021 18:47:20 +0200 Subject: [PATCH 33/84] clean --- xcm/xcm-simulator/example/src/lib.rs | 97 ++++++++++++++++------------ 1 file changed, 54 insertions(+), 43 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index d39d0a9ee523..11e84d87cf30 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -109,13 +109,13 @@ mod tests { MultiAsset::*, MultiLocation::*, NetworkId, Order, OriginKind, + Response::Assets, Xcm::*, }; use xcm_simulator::TestExt; // Helper function for forming buy execution message fn buy_execution(debt: Weight) -> Order { - use xcm::opaque::v0::prelude::*; Order::BuyExecution { fees: All, weight: 0, debt, halt_on_error: false, xcm: vec![] } } @@ -208,35 +208,33 @@ mod tests { fn reserve_transfer_assets() { MockNet::reset(); + let withdraw_amount = 123; + let max_weight_for_execution = 10; + Relay::execute_with(|| { assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( relay_chain::Origin::signed(ALICE), X1(Parachain(1)), X1(Junction::AccountId32 { network: NetworkId::Any, id: ALICE.into() }), - vec![ConcreteFungible { id: Null, amount: 123 }], - 123, + vec![ConcreteFungible { id: Null, amount: withdraw_amount }], + max_weight_for_execution, )); let para_account_a = ParaId::from(1).into_account(); - assert_eq!(parachain::Balances::free_balance(¶_account_a), INITIAL_BALANCE + 123); + assert_eq!( + parachain::Balances::free_balance(¶_account_a), + INITIAL_BALANCE + withdraw_amount + ); }); ParaA::execute_with(|| { - use xcm::opaque::v0::NetworkId; - use xcm_simulator::{BuyExecution, DepositAsset}; - // check message received + // Check message received let expected_message = ( X1(Parent), ReserveAssetDeposit { - assets: vec![ConcreteFungible { id: X1(Parent), amount: 123 }], + assets: vec![ConcreteFungible { id: X1(Parent), amount: withdraw_amount }], effects: vec![ - BuyExecution { - fees: All, - weight: 0, - debt: 123, - halt_on_error: false, - xcm: vec![], - }, - DepositAsset { + buy_execution(max_weight_for_execution), + Order::DepositAsset { assets: vec![All], dest: X1(Junction::AccountId32 { network: NetworkId::Any, @@ -247,29 +245,31 @@ mod tests { }, ); assert_eq!(parachain::MsgQueue::received_dmp(), vec![expected_message]); - // check message execution with full amount received + // Check message execution with full amount received assert_eq!( pallet_balances::Pallet::::free_balance(&ALICE), - INITIAL_BALANCE + 123 + INITIAL_BALANCE + withdraw_amount ); }); } /// Scenario: - /// A parachain transfers funds on the relaychain to another parachain's account. + /// A parachain transfers funds on the relay chain to another parachain account. /// /// Asserts that the parachain accounts are updated as expected. #[test] fn withdraw_and_deposit() { MockNet::reset(); + let send_amount = 10; + let mut amount_received = 0; + let weight_for_execution = 3 * relay_chain::BaseXcmWeight::get(); + ParaA::execute_with(|| { - let amount = 10; - let weight = 3 * relay_chain::BaseXcmWeight::get(); let message = WithdrawAsset { - assets: vec![ConcreteFungible { id: Null, amount }], + assets: vec![ConcreteFungible { id: Null, amount: send_amount }], effects: vec![ - buy_execution(weight), + buy_execution(weight_for_execution), Order::DepositAsset { assets: vec![All], dest: Parachain(2).into() }, ], }; @@ -277,15 +277,16 @@ mod tests { assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); }); + amount_received += send_amount; + Relay::execute_with(|| { - let amount = 10; let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); assert_eq!( relay_chain::Balances::free_balance(para_account_a), - INITIAL_BALANCE - amount + INITIAL_BALANCE - send_amount ); let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); - assert_eq!(relay_chain::Balances::free_balance(para_account_b), 10); + assert_eq!(relay_chain::Balances::free_balance(para_account_b), amount_received); }); } @@ -298,16 +299,20 @@ mod tests { fn query_holding() { MockNet::reset(); + let send_amount = 10; + let mut amount_spent = 0; + let mut amount_received = 0; + let weight_for_execution = 3 * relay_chain::BaseXcmWeight::get(); + let query_id_set = 1234; + // First send a message which fails on the relay chain ParaA::execute_with(|| { - let amount = 10; - let weight = 3 * relay_chain::BaseXcmWeight::get(); //let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); let query_id = 1234; let message = WithdrawAsset { - assets: vec![ConcreteFungible { id: Null, amount }], + assets: vec![ConcreteFungible { id: Null, amount: send_amount }], effects: vec![ - buy_execution(weight), + buy_execution(weight_for_execution), Order::DepositAsset { assets: vec![All], dest: OnlyChild.into(), // invalid destination @@ -320,14 +325,15 @@ mod tests { assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); }); + amount_spent += send_amount; + // Check that no transfer was executed and no response message was sent Relay::execute_with(|| { - let amount = 10; let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); // withdraw did execute assert_eq!( relay_chain::Balances::free_balance(para_account_a), - INITIAL_BALANCE - amount + INITIAL_BALANCE - amount_spent ); // but deposit did not execute let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); @@ -336,44 +342,49 @@ mod tests { // Now send a message which fully succeeds on the relay chain ParaA::execute_with(|| { - let amount = 10; - let weight = 3 * relay_chain::BaseXcmWeight::get(); - let query_id = 1234; let message = WithdrawAsset { - assets: vec![ConcreteFungible { id: Null, amount }], + assets: vec![ConcreteFungible { id: Null, amount: send_amount }], effects: vec![ - buy_execution(weight), + buy_execution(weight_for_execution), Order::DepositAsset { assets: vec![All], dest: Parachain(2).into(), // valid destination }, - Order::QueryHolding { query_id, dest: Parachain(1).into(), assets: vec![All] }, + Order::QueryHolding { + query_id: query_id_set, + dest: Parachain(1).into(), + assets: vec![All], + }, ], }; // Send withdraw and deposit with query holding assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); }); + amount_spent += send_amount; + amount_received += send_amount; + // Check that transfer was executed Relay::execute_with(|| { - let spent = 20; let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); // withdraw did execute assert_eq!( relay_chain::Balances::free_balance(para_account_a), - INITIAL_BALANCE - spent + INITIAL_BALANCE - amount_spent ); // and deposit did execute let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); - assert_eq!(relay_chain::Balances::free_balance(para_account_b), 10); + assert_eq!(relay_chain::Balances::free_balance(para_account_b), amount_received); }); // Check that QueryResponse message was received ParaA::execute_with(|| { - use xcm::opaque::v0::Response::Assets; assert_eq!( parachain::MsgQueue::received_dmp(), - vec![(X1(Parent), QueryResponse { query_id: 1234, response: Assets(vec![]) })] + vec![( + X1(Parent), + QueryResponse { query_id: query_id_set, response: Assets(vec![]) } + )] ); }); } From 287cd76e78cc5fa025c87cba8089ebfb4b93ee71 Mon Sep 17 00:00:00 2001 From: Amar Singh Date: Mon, 2 Aug 2021 12:48:45 -0400 Subject: [PATCH 34/84] Update xcm/xcm-simulator/src/lib.rs Co-authored-by: Alexander Popiak --- xcm/xcm-simulator/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 66cd704d1ffc..919eaf8f1309 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -278,6 +278,7 @@ macro_rules! decl_test_network { /// XCM router for relay chain. pub struct RelayChainXcmRouter; + /// Process all messages originating from the relay chain. fn process_relay_messages() -> $crate::XcmResult { use $crate::DmpMessageHandlerT; @@ -318,4 +319,4 @@ macro_rules! decl_test_network { } } }; -} \ No newline at end of file +} From 830593487fb307f214107ff413f2f4371cedb6c4 Mon Sep 17 00:00:00 2001 From: Amar Singh Date: Mon, 2 Aug 2021 12:48:52 -0400 Subject: [PATCH 35/84] Update xcm/xcm-simulator/src/lib.rs Co-authored-by: Alexander Popiak --- xcm/xcm-simulator/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 919eaf8f1309..6771f52f6119 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -218,6 +218,7 @@ macro_rules! decl_test_network { /// XCM router for parachain. pub struct ParachainXcmRouter($crate::PhantomData); + /// Process all messages originating from parachains. fn process_para_messages() -> $crate::XcmResult { use $crate::{UmpSink, XcmpMessageHandlerT}; From 0444d45372f0358808dcc921334ddb5bf1799635 Mon Sep 17 00:00:00 2001 From: Amar Singh Date: Mon, 2 Aug 2021 12:49:07 -0400 Subject: [PATCH 36/84] Update xcm/xcm-simulator/src/lib.rs Co-authored-by: Alexander Popiak --- xcm/xcm-simulator/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 6771f52f6119..f89430879508 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -210,6 +210,9 @@ macro_rules! decl_test_network { } } + /// Process any XCMs in the message buses. + /// + /// Called automatically by `TestExt::execute_with`. fn process_messages() -> $crate::XcmResult { process_relay_messages()?; process_para_messages() From 8e60c1164418f928ef2a6a3ac4713e84baa98eaa Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 2 Aug 2021 19:17:48 +0200 Subject: [PATCH 37/84] spellcheck nit --- xcm/xcm-simulator/example/src/parachain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index 854bf1906eeb..3f03367d4406 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -193,7 +193,7 @@ pub mod mock_msg_queue { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { // XCMP - /// Some XCM was executed ok. + /// Some XCM was executed OK. Success(Option), /// Some XCM failed. Fail(Option, XcmError), From daed1fdc4d5deba432b3bf591068f843eb9061eb Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 2 Aug 2021 19:40:06 +0200 Subject: [PATCH 38/84] clean --- xcm/xcm-simulator/example/src/lib.rs | 8 +++++--- xcm/xcm-simulator/src/lib.rs | 11 ++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 11e84d87cf30..638e82b1f2c2 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -307,8 +307,6 @@ mod tests { // First send a message which fails on the relay chain ParaA::execute_with(|| { - //let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); - let query_id = 1234; let message = WithdrawAsset { assets: vec![ConcreteFungible { id: Null, amount: send_amount }], effects: vec![ @@ -318,7 +316,11 @@ mod tests { dest: OnlyChild.into(), // invalid destination }, // is not triggered because the deposit fails - Order::QueryHolding { query_id, dest: Parachain(2).into(), assets: vec![All] }, + Order::QueryHolding { + query_id: query_id_set, + dest: Parachain(2).into(), + assets: vec![All], + }, ], }; // Send withdraw and deposit with query holding diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index f89430879508..bb907a088daf 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -20,9 +20,6 @@ pub use codec::{Decode, Encode}; pub use paste; pub use frame_support::{traits::Get, weights::Weight}; -pub use sp_io::TestExternalities; -pub use sp_std::{cell::RefCell, marker::PhantomData}; - pub use polkadot_core_primitives::BlockNumber as RelayBlockNumber; pub use polkadot_parachain::primitives::{ DmpMessageHandler as DmpMessageHandlerT, Id as ParaId, XcmpMessageFormat, @@ -32,6 +29,8 @@ pub use polkadot_runtime_parachains::{ dmp, ump::{self, MessageId, UmpSink, XcmSink}, }; +pub use sp_io::TestExternalities; +pub use sp_std::{cell::RefCell, collections::vec_deque::VecDeque, marker::PhantomData}; pub use xcm::{v0::prelude::*, VersionedXcm}; pub use xcm_executor::XcmExecutor; @@ -92,8 +91,8 @@ macro_rules! decl_test_relay_chain { Self::execute_with(|| { $crate::ump::XcmSink::<$crate::XcmExecutor<$xcm_config>, $runtime>::process_upward_message( - origin, msg, max_weight, - ) + origin, msg, max_weight, + ) }) } } @@ -182,8 +181,6 @@ macro_rules! __impl_ext { }; } -use sp_std::collections::vec_deque::VecDeque; - thread_local! { pub static PARA_MESSAGE_BUS: RefCell)>> = RefCell::new(VecDeque::new()); From 68c725f4a45036979654788cacf5db3480a3ea62 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 2 Aug 2021 19:42:37 +0200 Subject: [PATCH 39/84] fix fmt in macro --- xcm/xcm-simulator/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index bb907a088daf..2956904f94ef 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -91,8 +91,8 @@ macro_rules! decl_test_relay_chain { Self::execute_with(|| { $crate::ump::XcmSink::<$crate::XcmExecutor<$xcm_config>, $runtime>::process_upward_message( - origin, msg, max_weight, - ) + origin, msg, max_weight, + ) }) } } From fa4bac12d05d12a25eae7130b407fe9de8185d08 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Tue, 3 Aug 2021 10:52:26 +0200 Subject: [PATCH 40/84] address review comments --- xcm/xcm-simulator/src/lib.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 2956904f94ef..0525b9ab1a7d 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -174,7 +174,11 @@ macro_rules! __impl_ext { fn execute_with(execute: impl FnOnce() -> R) -> R { let result = $ext_name.with(|v| v.borrow_mut().execute_with(execute)); - process_messages().expect("message processing failure"); + while exists_messages_in_any_bus() { + if let Err(xcm_error) = process_messages() { + panic!("Message processing failure: {:?}", xcm_error); + } + } result } } @@ -200,13 +204,26 @@ macro_rules! decl_test_network { impl $name { pub fn reset() { - use $crate::TestExt; - + use $crate::{TestExt, VecDeque}; + // Reset relay chain message bus + $crate::RELAY_MESSAGE_BUS.with(|b| b.replace(VecDeque::new())); + // Reset parachain message bus + $crate::PARA_MESSAGE_BUS.with(|b| b.replace(VecDeque::new())); + // Reset relay chain state <$relay_chain>::reset_ext(); + // Reset parachain state $( <$parachain>::reset_ext(); )* } } + /// Check if any messages exist in either message bus + fn exists_messages_in_any_bus() -> bool { + use $crate::{RELAY_MESSAGE_BUS, PARA_MESSAGE_BUS}; + let no_relay_messages_left = RELAY_MESSAGE_BUS.with(|b| b.borrow().is_empty()); + let no_parachain_messages_left = PARA_MESSAGE_BUS.with(|b| b.borrow().is_empty()); + !(no_relay_messages_left && no_parachain_messages_left) + } + /// Process any XCMs in the message buses. /// /// Called automatically by `TestExt::execute_with`. From 97e3bd6abacba5ea42f9fcbe353a4acf48e31d8c Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Tue, 3 Aug 2021 13:03:24 +0200 Subject: [PATCH 41/84] move process all messages to trait implementation so execute with uses default impl defined in trait defn --- xcm/xcm-simulator/src/lib.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 0525b9ab1a7d..268ea57aae3d 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -39,13 +39,20 @@ pub trait TestExt { fn new_ext() -> sp_io::TestExternalities; /// Resets the state of the test environment. fn reset_ext(); - /// Execute some code in the context of the test externalities, with manual - /// message processing which requires calling `process_messages()`. + /// Execute code in the context of the test externalities, without automatic + /// message processing. All messages in the message buses can be processed + /// by calling `Self::process_all_messages()`. fn execute_without_auto_dispatch(execute: impl FnOnce() -> R) -> R; + /// Process all messages in the message buses + fn process_all_messages(); /// Execute some code in the context of the test externalities, with - /// automatic processing of messages. + /// automatic message processing. /// Messages are dispatched once the passed closure completes. - fn execute_with(execute: impl FnOnce() -> R) -> R; + fn execute_with(execute: impl FnOnce() -> R) -> R { + let result = Self::execute_without_auto_dispatch(execute); + Self::process_all_messages(); + result + } } pub enum MessageKind { @@ -172,14 +179,12 @@ macro_rules! __impl_ext { $ext_name.with(|v| v.borrow_mut().execute_with(execute)) } - fn execute_with(execute: impl FnOnce() -> R) -> R { - let result = $ext_name.with(|v| v.borrow_mut().execute_with(execute)); + fn process_all_messages() { while exists_messages_in_any_bus() { if let Err(xcm_error) = process_messages() { panic!("Message processing failure: {:?}", xcm_error); } } - result } } }; From 91cccf5aea7ace2cc6c23281fe3dc0db6cb49e9a Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Tue, 3 Aug 2021 16:49:45 +0200 Subject: [PATCH 42/84] better naming and organization --- rust-toolchain | 5 ++ xcm/xcm-executor/src/lib.rs | 1 + xcm/xcm-simulator/example/src/lib.rs | 30 +++++----- xcm/xcm-simulator/src/lib.rs | 89 +++++++++++++--------------- 4 files changed, 63 insertions(+), 62 deletions(-) create mode 100644 rust-toolchain diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 000000000000..ae16315aa35e --- /dev/null +++ b/rust-toolchain @@ -0,0 +1,5 @@ +[toolchain] +channel = "nightly-2021-02-24" +components = [ "rustfmt", "clippy" ] +targets = [ "wasm32-unknown-unknown" ] +profile = "minimal" diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 8f8a5c9ee617..f8fdff04ef36 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -15,6 +15,7 @@ // along with Polkadot. If not, see . #![cfg_attr(not(feature = "std"), no_std)] +#![feature(or_patterns)] use sp_std::{prelude::*, marker::PhantomData}; use frame_support::{ diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 638e82b1f2c2..766cb0454bae 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -126,7 +126,7 @@ mod tests { let remark = parachain::Call::System( frame_system::Call::::remark_with_event(vec![1, 2, 3]), ); - Relay::execute_with(|| { + Relay::execute_and_dispatch_xcm(|| { assert_ok!(RelayChainPalletXcm::send_xcm( Null, X1(Parachain(1)), @@ -138,7 +138,7 @@ mod tests { )); }); - ParaA::execute_with(|| { + ParaA::execute_and_dispatch_xcm(|| { use parachain::{Event, System}; assert!(System::events() .iter() @@ -153,7 +153,7 @@ mod tests { let remark = relay_chain::Call::System( frame_system::Call::::remark_with_event(vec![1, 2, 3]), ); - ParaA::execute_with(|| { + ParaA::execute_and_dispatch_xcm(|| { assert_ok!(ParachainPalletXcm::send_xcm( Null, X1(Parent), @@ -165,7 +165,7 @@ mod tests { )); }); - Relay::execute_with(|| { + Relay::execute_and_dispatch_xcm(|| { use relay_chain::{Event, System}; assert!(System::events() .iter() @@ -180,7 +180,7 @@ mod tests { let remark = parachain::Call::System( frame_system::Call::::remark_with_event(vec![1, 2, 3]), ); - ParaA::execute_with(|| { + ParaA::execute_and_dispatch_xcm(|| { assert_ok!(ParachainPalletXcm::send_xcm( Null, X2(Parent, Parachain(2)), @@ -192,7 +192,7 @@ mod tests { )); }); - ParaB::execute_with(|| { + ParaB::execute_and_dispatch_xcm(|| { use parachain::{Event, System}; assert!(System::events() .iter() @@ -211,7 +211,7 @@ mod tests { let withdraw_amount = 123; let max_weight_for_execution = 10; - Relay::execute_with(|| { + Relay::execute_and_dispatch_xcm(|| { assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( relay_chain::Origin::signed(ALICE), X1(Parachain(1)), @@ -226,7 +226,7 @@ mod tests { ); }); - ParaA::execute_with(|| { + ParaA::execute_and_dispatch_xcm(|| { // Check message received let expected_message = ( X1(Parent), @@ -265,7 +265,7 @@ mod tests { let mut amount_received = 0; let weight_for_execution = 3 * relay_chain::BaseXcmWeight::get(); - ParaA::execute_with(|| { + ParaA::execute_and_dispatch_xcm(|| { let message = WithdrawAsset { assets: vec![ConcreteFungible { id: Null, amount: send_amount }], effects: vec![ @@ -279,7 +279,7 @@ mod tests { amount_received += send_amount; - Relay::execute_with(|| { + Relay::execute_and_dispatch_xcm(|| { let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); assert_eq!( relay_chain::Balances::free_balance(para_account_a), @@ -306,7 +306,7 @@ mod tests { let query_id_set = 1234; // First send a message which fails on the relay chain - ParaA::execute_with(|| { + ParaA::execute_and_dispatch_xcm(|| { let message = WithdrawAsset { assets: vec![ConcreteFungible { id: Null, amount: send_amount }], effects: vec![ @@ -330,7 +330,7 @@ mod tests { amount_spent += send_amount; // Check that no transfer was executed and no response message was sent - Relay::execute_with(|| { + Relay::execute_and_dispatch_xcm(|| { let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); // withdraw did execute assert_eq!( @@ -343,7 +343,7 @@ mod tests { }); // Now send a message which fully succeeds on the relay chain - ParaA::execute_with(|| { + ParaA::execute_and_dispatch_xcm(|| { let message = WithdrawAsset { assets: vec![ConcreteFungible { id: Null, amount: send_amount }], effects: vec![ @@ -367,7 +367,7 @@ mod tests { amount_received += send_amount; // Check that transfer was executed - Relay::execute_with(|| { + Relay::execute_and_dispatch_xcm(|| { let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); // withdraw did execute assert_eq!( @@ -380,7 +380,7 @@ mod tests { }); // Check that QueryResponse message was received - ParaA::execute_with(|| { + ParaA::execute_and_dispatch_xcm(|| { assert_eq!( parachain::MsgQueue::received_dmp(), vec![( diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 268ea57aae3d..bed2f60b8d13 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -42,15 +42,15 @@ pub trait TestExt { /// Execute code in the context of the test externalities, without automatic /// message processing. All messages in the message buses can be processed /// by calling `Self::process_all_messages()`. - fn execute_without_auto_dispatch(execute: impl FnOnce() -> R) -> R; + fn execute_and_queue_xcm(execute: impl FnOnce() -> R) -> R; /// Process all messages in the message buses - fn process_all_messages(); + fn dispatch_xcm_queue(); /// Execute some code in the context of the test externalities, with /// automatic message processing. /// Messages are dispatched once the passed closure completes. - fn execute_with(execute: impl FnOnce() -> R) -> R { - let result = Self::execute_without_auto_dispatch(execute); - Self::process_all_messages(); + fn execute_and_dispatch_xcm(execute: impl FnOnce() -> R) -> R { + let result = Self::execute_and_queue_xcm(execute); + Self::dispatch_xcm_queue(); result } } @@ -96,7 +96,7 @@ macro_rules! decl_test_relay_chain { ) -> Result<$crate::Weight, ($crate::MessageId, $crate::Weight)> { use $crate::{ump::UmpSink, TestExt}; - Self::execute_with(|| { + Self::execute_and_dispatch_xcm(|| { $crate::ump::XcmSink::<$crate::XcmExecutor<$xcm_config>, $runtime>::process_upward_message( origin, msg, max_weight, ) @@ -130,7 +130,7 @@ macro_rules! decl_test_parachain { ) -> $crate::Weight { use $crate::{TestExt, XcmpMessageHandlerT}; - $name::execute_with(|| { + $name::execute_and_dispatch_xcm(|| { <$xcmp_message_handler>::handle_xcmp_messages(iter, max_weight) }) } @@ -143,7 +143,7 @@ macro_rules! decl_test_parachain { ) -> $crate::Weight { use $crate::{DmpMessageHandlerT, TestExt}; - $name::execute_with(|| { + $name::execute_and_dispatch_xcm(|| { <$dmp_message_handler>::handle_dmp_messages(iter, max_weight) }) } @@ -175,14 +175,17 @@ macro_rules! __impl_ext { $ext_name.with(|v| *v.borrow_mut() = $new_ext); } - fn execute_without_auto_dispatch(execute: impl FnOnce() -> R) -> R { + fn execute_and_queue_xcm(execute: impl FnOnce() -> R) -> R { $ext_name.with(|v| v.borrow_mut().execute_with(execute)) } - fn process_all_messages() { + fn dispatch_xcm_queue() { while exists_messages_in_any_bus() { - if let Err(xcm_error) = process_messages() { - panic!("Message processing failure: {:?}", xcm_error); + if let Err(xcm_error) = process_relay_messages() { + panic!("Relay chain XCM execution failure: {:?}", xcm_error); + } + if let Err(xcm_error) = process_para_messages() { + panic!("Parachain XCM execution failure: {:?}", xcm_error); } } } @@ -229,17 +232,6 @@ macro_rules! decl_test_network { !(no_relay_messages_left && no_parachain_messages_left) } - /// Process any XCMs in the message buses. - /// - /// Called automatically by `TestExt::execute_with`. - fn process_messages() -> $crate::XcmResult { - process_relay_messages()?; - process_para_messages() - } - - /// XCM router for parachain. - pub struct ParachainXcmRouter($crate::PhantomData); - /// Process all messages originating from parachains. fn process_para_messages() -> $crate::XcmResult { use $crate::{UmpSink, XcmpMessageHandlerT}; @@ -276,6 +268,33 @@ macro_rules! decl_test_network { Ok(()) } + /// Process all messages originating from the relay chain. + fn process_relay_messages() -> $crate::XcmResult { + use $crate::DmpMessageHandlerT; + + while let Some((destination, message)) = $crate::RELAY_MESSAGE_BUS.with( + |b| b.borrow_mut().pop_front()) { + match destination { + $( + $crate::X1($crate::Parachain(id)) if id == $para_id => { + let encoded = $crate::encode_xcm(message, $crate::MessageKind::Dmp); + // NOTE: RelayChainBlockNumber is hard-coded to 1 + let messages = vec![(1, encoded)]; + let _weight = <$parachain>::handle_dmp_messages( + messages.into_iter(), $crate::Weight::max_value(), + ); + }, + )* + _ => return Err($crate::XcmError::SendFailed("Only sends to children parachain.")), + } + } + + Ok(()) + } + + /// XCM router for parachain. + pub struct ParachainXcmRouter($crate::PhantomData); + impl> $crate::SendXcm for ParachainXcmRouter { fn send_xcm(destination: $crate::MultiLocation, message: $crate::Xcm<()>) -> $crate::XcmResult { use $crate::{UmpSink, XcmpMessageHandlerT}; @@ -301,30 +320,6 @@ macro_rules! decl_test_network { /// XCM router for relay chain. pub struct RelayChainXcmRouter; - /// Process all messages originating from the relay chain. - fn process_relay_messages() -> $crate::XcmResult { - use $crate::DmpMessageHandlerT; - - while let Some((destination, message)) = $crate::RELAY_MESSAGE_BUS.with( - |b| b.borrow_mut().pop_front()) { - match destination { - $( - $crate::X1($crate::Parachain(id)) if id == $para_id => { - let encoded = $crate::encode_xcm(message, $crate::MessageKind::Dmp); - // NOTE: RelayChainBlockNumber is hard-coded to 1 - let messages = vec![(1, encoded)]; - let _weight = <$parachain>::handle_dmp_messages( - messages.into_iter(), $crate::Weight::max_value(), - ); - }, - )* - _ => return Err($crate::XcmError::SendFailed("Only sends to children parachain.")), - } - } - - Ok(()) - } - impl $crate::SendXcm for RelayChainXcmRouter { fn send_xcm(destination: $crate::MultiLocation, message: $crate::Xcm<()>) -> $crate::XcmResult { use $crate::DmpMessageHandlerT; From e5cb209f1a1b7bfa4a0248c5f5f714a285225b2c Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Tue, 3 Aug 2021 16:50:07 +0200 Subject: [PATCH 43/84] rm local env --- rust-toolchain | 5 ----- xcm/xcm-executor/src/lib.rs | 1 - 2 files changed, 6 deletions(-) delete mode 100644 rust-toolchain diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index ae16315aa35e..000000000000 --- a/rust-toolchain +++ /dev/null @@ -1,5 +0,0 @@ -[toolchain] -channel = "nightly-2021-02-24" -components = [ "rustfmt", "clippy" ] -targets = [ "wasm32-unknown-unknown" ] -profile = "minimal" diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index f8fdff04ef36..8f8a5c9ee617 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -15,7 +15,6 @@ // along with Polkadot. If not, see . #![cfg_attr(not(feature = "std"), no_std)] -#![feature(or_patterns)] use sp_std::{prelude::*, marker::PhantomData}; use frame_support::{ From 847b99a922153ab8f0998c992bdb756691d3077b Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Tue, 3 Aug 2021 16:52:05 +0200 Subject: [PATCH 44/84] fix docs --- xcm/xcm-simulator/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index bed2f60b8d13..0bc596eeca0f 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -41,7 +41,7 @@ pub trait TestExt { fn reset_ext(); /// Execute code in the context of the test externalities, without automatic /// message processing. All messages in the message buses can be processed - /// by calling `Self::process_all_messages()`. + /// by calling `Self::dispatch_xcm_queue()`. fn execute_and_queue_xcm(execute: impl FnOnce() -> R) -> R; /// Process all messages in the message buses fn dispatch_xcm_queue(); From 97051b283461f486d00fd59842c79b7161d33684 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 3 Aug 2021 17:47:03 +0200 Subject: [PATCH 45/84] Update xcm/src/v0/multiasset.rs Co-authored-by: Alexander Popiak --- xcm/src/v0/multiasset.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index 351665baaa0c..944e449b60f9 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -272,6 +272,7 @@ pub enum WildFungibility { NonFungible, } +/// A wildcard representing a set of assets. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)] pub enum WildMultiAsset { /// All assets in the holding register, up to `usize` individual assets (different instances of non-fungibles could From 84b79cf71ad03191e5f9c62cc863c265f9d70e70 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 3 Aug 2021 17:49:07 +0200 Subject: [PATCH 46/84] Update xcm/src/v0/multiasset.rs Co-authored-by: Alexander Popiak --- xcm/src/v0/multiasset.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index 944e449b60f9..dcd1d9f905c6 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -315,8 +315,10 @@ impl, B: Into> From<(A, B)> for WildMultiAsset -/// `MultiAsset` collection, either `MultiAssets` or a single wildcard. Note: vectors of wildcards -/// whose encoding is supported in XCM v0 are unsupported in this implementation and will result in a decode error. +/// `MultiAsset` collection, either `MultiAssets` or a single wildcard. +/// +/// Note: Vectors of wildcards whose encoding is supported in XCM v0 are unsupported +/// in this implementation and will result in a decode error. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)] pub enum MultiAssetFilter { Definite(MultiAssets), From 1f988728108b30ecf893b716bd0406546f578b76 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 3 Aug 2021 17:49:27 +0200 Subject: [PATCH 47/84] Update xcm/src/v0/multiasset.rs Co-authored-by: Alexander Popiak --- xcm/src/v0/multiasset.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index dcd1d9f905c6..0f3ea7f1ec8e 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -260,11 +260,6 @@ impl MultiAssets { self.0.iter_mut().try_for_each(|i| i.reanchor(prepend)) } } - - - - - /// Classification of whether an asset is fungible or not, along with an optional amount or instance. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)] pub enum WildFungibility { From 0cc61debb05c30e394c1179b8a514f433c8ca325 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 3 Aug 2021 17:49:32 +0200 Subject: [PATCH 48/84] Update xcm/src/v0/multiasset.rs Co-authored-by: Alexander Popiak --- xcm/src/v0/multiasset.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index 0f3ea7f1ec8e..02c43fc1d205 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -306,10 +306,6 @@ impl, B: Into> From<(A, B)> for WildMultiAsset WildMultiAsset::AllOf { fun: fun.into(), id: id.into() } } } - - - - /// `MultiAsset` collection, either `MultiAssets` or a single wildcard. /// /// Note: Vectors of wildcards whose encoding is supported in XCM v0 are unsupported From a2dc4853ced2126440beabd1ffae5f3f9f50344c Mon Sep 17 00:00:00 2001 From: Amar Singh Date: Tue, 3 Aug 2021 12:46:01 -0400 Subject: [PATCH 49/84] Update xcm/xcm-simulator/example/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- xcm/xcm-simulator/example/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 766cb0454bae..46bf1764295f 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -274,7 +274,7 @@ mod tests { ], }; // Send withdraw and deposit - assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); + assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone())); }); amount_received += send_amount; From 0778e76959fcbb7887154a62b02412e0ec699671 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Tue, 3 Aug 2021 18:58:04 +0200 Subject: [PATCH 50/84] follow review suggestions --- xcm/xcm-simulator/example/src/lib.rs | 81 ++++++---------------------- 1 file changed, 16 insertions(+), 65 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 46bf1764295f..01030833c6cc 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -21,8 +21,6 @@ use polkadot_parachain::primitives::Id as ParaId; use sp_runtime::{traits::AccountIdConversion, AccountId32}; use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; -pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]); - decl_test_parachain! { pub struct ParaA { Runtime = parachain::Runtime, @@ -59,8 +57,13 @@ decl_test_network! { } } +pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]); pub const INITIAL_BALANCE: u128 = 1_000_000_000; +pub fn para_account_id(id: u32) -> relay_chain::AccountId { + ParaId::from(id).into_account() +} + pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { use parachain::{MsgQueue, Runtime, System}; @@ -80,11 +83,10 @@ pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { pub fn relay_ext() -> sp_io::TestExternalities { use relay_chain::{Runtime, System}; - let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig:: { - balances: vec![(ALICE, INITIAL_BALANCE), (para_account_a, INITIAL_BALANCE)], + balances: vec![(ALICE, INITIAL_BALANCE), (para_account_id(1), INITIAL_BALANCE)], } .assimilate_storage(&mut t) .unwrap(); @@ -104,7 +106,6 @@ mod tests { use codec::Encode; use frame_support::{assert_ok, weights::Weight}; use xcm::v0::{ - prelude::OnlyChild, Junction::{self, Parachain, Parent}, MultiAsset::*, MultiLocation::*, @@ -219,9 +220,8 @@ mod tests { vec![ConcreteFungible { id: Null, amount: withdraw_amount }], max_weight_for_execution, )); - let para_account_a = ParaId::from(1).into_account(); assert_eq!( - parachain::Balances::free_balance(¶_account_a), + parachain::Balances::free_balance(¶_account_id(1)), INITIAL_BALANCE + withdraw_amount ); }); @@ -280,13 +280,11 @@ mod tests { amount_received += send_amount; Relay::execute_and_dispatch_xcm(|| { - let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); assert_eq!( - relay_chain::Balances::free_balance(para_account_a), + relay_chain::Balances::free_balance(para_account_id(1)), INITIAL_BALANCE - send_amount ); - let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); - assert_eq!(relay_chain::Balances::free_balance(para_account_b), amount_received); + assert_eq!(relay_chain::Balances::free_balance(para_account_id(2)), amount_received); }); } @@ -300,58 +298,16 @@ mod tests { MockNet::reset(); let send_amount = 10; - let mut amount_spent = 0; - let mut amount_received = 0; let weight_for_execution = 3 * relay_chain::BaseXcmWeight::get(); let query_id_set = 1234; - // First send a message which fails on the relay chain + // Send a message which fully succeeds on the relay chain ParaA::execute_and_dispatch_xcm(|| { let message = WithdrawAsset { assets: vec![ConcreteFungible { id: Null, amount: send_amount }], effects: vec![ buy_execution(weight_for_execution), - Order::DepositAsset { - assets: vec![All], - dest: OnlyChild.into(), // invalid destination - }, - // is not triggered because the deposit fails - Order::QueryHolding { - query_id: query_id_set, - dest: Parachain(2).into(), - assets: vec![All], - }, - ], - }; - // Send withdraw and deposit with query holding - assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); - }); - - amount_spent += send_amount; - - // Check that no transfer was executed and no response message was sent - Relay::execute_and_dispatch_xcm(|| { - let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); - // withdraw did execute - assert_eq!( - relay_chain::Balances::free_balance(para_account_a), - INITIAL_BALANCE - amount_spent - ); - // but deposit did not execute - let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); - assert_eq!(relay_chain::Balances::free_balance(para_account_b), 0); - }); - - // Now send a message which fully succeeds on the relay chain - ParaA::execute_and_dispatch_xcm(|| { - let message = WithdrawAsset { - assets: vec![ConcreteFungible { id: Null, amount: send_amount }], - effects: vec![ - buy_execution(weight_for_execution), - Order::DepositAsset { - assets: vec![All], - dest: Parachain(2).into(), // valid destination - }, + Order::DepositAsset { assets: vec![All], dest: Parachain(2).into() }, Order::QueryHolding { query_id: query_id_set, dest: Parachain(1).into(), @@ -363,20 +319,15 @@ mod tests { assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); }); - amount_spent += send_amount; - amount_received += send_amount; - // Check that transfer was executed Relay::execute_and_dispatch_xcm(|| { - let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); - // withdraw did execute + // Withdraw executed assert_eq!( - relay_chain::Balances::free_balance(para_account_a), - INITIAL_BALANCE - amount_spent + relay_chain::Balances::free_balance(para_account_id(1)), + INITIAL_BALANCE - send_amount ); - // and deposit did execute - let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); - assert_eq!(relay_chain::Balances::free_balance(para_account_b), amount_received); + // Deposit executed + assert_eq!(relay_chain::Balances::free_balance(para_account_id(2)), send_amount); }); // Check that QueryResponse message was received From f164f1eaaacf8c35eae6d14ed38517cd6bd94593 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Tue, 3 Aug 2021 18:59:47 +0200 Subject: [PATCH 51/84] fix --- xcm/xcm-simulator/example/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 01030833c6cc..8dabff9809f5 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -262,7 +262,6 @@ mod tests { MockNet::reset(); let send_amount = 10; - let mut amount_received = 0; let weight_for_execution = 3 * relay_chain::BaseXcmWeight::get(); ParaA::execute_and_dispatch_xcm(|| { @@ -277,14 +276,12 @@ mod tests { assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone())); }); - amount_received += send_amount; - Relay::execute_and_dispatch_xcm(|| { assert_eq!( relay_chain::Balances::free_balance(para_account_id(1)), INITIAL_BALANCE - send_amount ); - assert_eq!(relay_chain::Balances::free_balance(para_account_id(2)), amount_received); + assert_eq!(relay_chain::Balances::free_balance(para_account_id(2)), send_amount); }); } From 723990cb50154b8b09a9a21a05c9b0c164bb945d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 3 Aug 2021 20:06:27 +0200 Subject: [PATCH 52/84] Reformat --- runtime/westend/src/lib.rs | 1 - xcm/pallet-xcm/src/lib.rs | 109 ++++++---- xcm/src/v0/mod.rs | 136 +++++++----- xcm/src/v0/multiasset.rs | 60 +++--- xcm/src/v0/order.rs | 52 +++-- xcm/xcm-builder/src/barriers.rs | 31 ++- xcm/xcm-builder/src/filter_asset_location.rs | 4 +- xcm/xcm-builder/src/fungibles_adapter.rs | 201 ++++++++++++------ xcm/xcm-builder/src/lib.rs | 19 +- xcm/xcm-builder/src/matches_fungible.rs | 10 +- xcm/xcm-builder/src/mock.rs | 116 +++++----- xcm/xcm-builder/src/tests.rs | 123 ++++++----- xcm/xcm-builder/src/weight.rs | 127 ++++++----- xcm/xcm-executor/src/assets.rs | 115 ++++++---- xcm/xcm-executor/src/lib.rs | 109 ++++++---- xcm/xcm-executor/src/traits/transact_asset.rs | 57 +++-- xcm/xcm-executor/src/traits/weight.rs | 14 +- 17 files changed, 753 insertions(+), 531 deletions(-) diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 2255f4194c87..12efa280771d 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -944,7 +944,6 @@ pub type LocalOriginToLocation = ( SignedToAccountId32, ); - impl pallet_xcm::Config for Runtime { type Event = Event; type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index 99341c2a0e85..95d5c867e781 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -18,23 +18,28 @@ #![cfg_attr(not(feature = "std"), no_std)] -use sp_std::{prelude::*, marker::PhantomData, convert::TryInto, boxed::Box, vec}; -use codec::{Encode, Decode}; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +use codec::{Decode, Encode}; +use frame_support::traits::{Contains, EnsureOrigin, Filter, Get, OriginTrait}; +use sp_runtime::{traits::BadOrigin, RuntimeDebug}; +use sp_std::{boxed::Box, convert::TryInto, marker::PhantomData, prelude::*, vec}; use xcm::v0::prelude::*; use xcm_executor::traits::ConvertOrigin; -use sp_runtime::{RuntimeDebug, traits::BadOrigin}; -use frame_support::traits::{EnsureOrigin, OriginTrait, Filter, Get, Contains}; -pub use pallet::*; use frame_support::PalletId; +pub use pallet::*; #[frame_support::pallet] pub mod pallet { use super::*; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - use xcm_executor::traits::WeightBounds; use sp_runtime::traits::AccountIdConversion; + use xcm_executor::traits::WeightBounds; #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] @@ -48,7 +53,7 @@ pub mod pallet { /// Required origin for sending XCM messages. If successful, the it resolves to `MultiLocation` /// which exists as an interior location within this chain's XCM context. - type SendXcmOrigin: EnsureOrigin; + type SendXcmOrigin: EnsureOrigin; /// The type used to actually dispatch an XCM to its destination. type XcmRouter: SendXcm; @@ -56,7 +61,7 @@ pub mod pallet { /// Required origin for executing XCM messages, including the teleport functionality. If successful, /// then it resolves to `MultiLocation` which exists as an interior location within this chain's XCM /// context. - type ExecuteXcmOrigin: EnsureOrigin; + type ExecuteXcmOrigin: EnsureOrigin; /// Our XCM filter which messages to be executed using `XcmExecutor` must pass. type XcmExecuteFilter: Contains<(MultiLocation, Xcm)>; @@ -101,11 +106,12 @@ pub mod pallet { #[pallet::weight(100_000_000)] pub fn send(origin: OriginFor, dest: MultiLocation, message: Xcm<()>) -> DispatchResult { let origin_location = T::SendXcmOrigin::ensure_origin(origin)?; - Self::send_xcm(origin_location.clone(), dest.clone(), message.clone()) - .map_err(|e| match e { + Self::send_xcm(origin_location.clone(), dest.clone(), message.clone()).map_err( + |e| match e { XcmError::CannotReachDestination(..) => Error::::Unreachable, _ => Error::::SendFailure, - })?; + }, + )?; Self::deposit_event(Event::Sent(origin_location, dest, message)); Ok(()) } @@ -147,27 +153,26 @@ pub mod pallet { let assets = assets.into(); let mut message = Xcm::WithdrawAsset { assets, - effects: vec![ - InitiateTeleport { - assets: Wild(All), - dest, - effects: vec![ - BuyExecution { - fees, - // Zero weight for additional XCM (since there are none to execute) - weight: 0, - debt: dest_weight, - halt_on_error: false, - xcm: vec![], - }, - DepositAsset { assets: Wild(All), dest: beneficiary }, - ], - }, - ], + effects: vec![InitiateTeleport { + assets: Wild(All), + dest, + effects: vec![ + BuyExecution { + fees, + // Zero weight for additional XCM (since there are none to execute) + weight: 0, + debt: dest_weight, + halt_on_error: false, + xcm: vec![], + }, + DepositAsset { assets: Wild(All), dest: beneficiary }, + ], + }], }; - let weight = T::Weigher::weight(&mut message) - .map_err(|()| Error::::UnweighableMessage)?; - let outcome = T::XcmExecutor::execute_xcm_in_credit(origin_location, message, weight, weight); + let weight = + T::Weigher::weight(&mut message).map_err(|()| Error::::UnweighableMessage)?; + let outcome = + T::XcmExecutor::execute_xcm_in_credit(origin_location, message, weight, weight); Self::deposit_event(Event::Attempted(outcome)); Ok(()) } @@ -220,9 +225,10 @@ pub mod pallet { DepositAsset { assets: Wild(All), dest: beneficiary }, ], }; - let weight = T::Weigher::weight(&mut message) - .map_err(|()| Error::::UnweighableMessage)?; - let outcome = T::XcmExecutor::execute_xcm_in_credit(origin_location, message, weight, weight); + let weight = + T::Weigher::weight(&mut message).map_err(|()| Error::::UnweighableMessage)?; + let outcome = + T::XcmExecutor::execute_xcm_in_credit(origin_location, message, weight, weight); Self::deposit_event(Event::Attempted(outcome)); Ok(()) } @@ -239,9 +245,11 @@ pub mod pallet { /// NOTE: A successful return to this does *not* imply that the `msg` was executed successfully /// to completion; only that *some* of it was executed. #[pallet::weight(max_weight.saturating_add(100_000_000u64))] - pub fn execute(origin: OriginFor, message: Box>, max_weight: Weight) - -> DispatchResult - { + pub fn execute( + origin: OriginFor, + message: Box>, + max_weight: Weight, + ) -> DispatchResult { let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; let value = (origin_location, *message); ensure!(T::XcmExecuteFilter::contains(&value), Error::::Filtered); @@ -255,7 +263,11 @@ pub mod pallet { impl Pallet { /// Relay an XCM `message` from a given `interior` location in this context to a given `dest` /// location. A null `dest` is not handled. - pub fn send_xcm(interior: MultiLocation, dest: MultiLocation, message: Xcm<()>) -> Result<(), XcmError> { + pub fn send_xcm( + interior: MultiLocation, + dest: MultiLocation, + message: Xcm<()>, + ) -> Result<(), XcmError> { let message = match interior { MultiLocation::Null => message, who => Xcm::<()>::RelayedFrom { who, message: Box::new(message) }, @@ -288,7 +300,8 @@ pub mod pallet { /// Ensure that the origin `o` represents a sibling parachain. /// Returns `Ok` with the parachain ID of the sibling or an `Err` otherwise. pub fn ensure_xcm(o: OuterOrigin) -> Result - where OuterOrigin: Into> +where + OuterOrigin: Into>, { match o.into() { Ok(Origin::Xcm(location)) => Ok(location), @@ -301,7 +314,9 @@ pub fn ensure_xcm(o: OuterOrigin) -> Result(PhantomData<(Prefix, Body)>); -impl, Body: Get> Filter for IsMajorityOfBody { +impl, Body: Get> Filter + for IsMajorityOfBody +{ fn filter(l: &MultiLocation) -> bool { let maybe_suffix = l.match_and_split(&Prefix::get()); matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part.is_majority()) @@ -312,19 +327,21 @@ impl, Body: Get> Filter for Is /// `Origin::Xcm` item. pub struct EnsureXcm(PhantomData); impl, F: Filter> EnsureOrigin for EnsureXcm - where O::PalletsOrigin: From + TryInto +where + O::PalletsOrigin: From + TryInto, { type Success = MultiLocation; fn try_origin(outer: O) -> Result { - outer.try_with_caller(|caller| caller.try_into() - .and_then(|Origin::Xcm(location)| + outer.try_with_caller(|caller| { + caller.try_into().and_then(|Origin::Xcm(location)| { if F::filter(&location) { Ok(location) } else { Err(Origin::Xcm(location).into()) } - )) + }) + }) } #[cfg(feature = "runtime-benchmarks")] @@ -336,9 +353,7 @@ impl, F: Filter> EnsureOrigin fo /// A simple passthrough where we reuse the `MultiLocation`-typed XCM origin as the inner value of /// this crate's `Origin::Xcm` value. pub struct XcmPassthrough(PhantomData); -impl< - Origin: From, -> ConvertOrigin for XcmPassthrough { +impl> ConvertOrigin for XcmPassthrough { fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result { match (kind, origin) { (OriginKind::Xcm, l) => Ok(crate::Origin::Xcm(l).into()), diff --git a/xcm/src/v0/mod.rs b/xcm/src/v0/mod.rs index 95bccfacf0b5..2bd568e60194 100644 --- a/xcm/src/v0/mod.rs +++ b/xcm/src/v0/mod.rs @@ -16,43 +16,52 @@ //! Version 0 of the Cross-Consensus Message format data structures. -use core::{result, convert::TryFrom, fmt::Debug}; -use derivative::Derivative; -use alloc::vec::Vec; -use parity_scale_codec::{self, Encode, Decode}; use crate::{DoubleEncoded, VersionedXcm}; +use alloc::vec::Vec; +use core::{convert::TryFrom, fmt::Debug, result}; +use derivative::Derivative; +use parity_scale_codec::{self, Decode, Encode}; mod junction; mod multi_location; +pub mod multiasset; mod order; -mod traits; -pub mod multiasset; // the new multiasset. +mod traits; // the new multiasset. -pub use junction::{Junction, NetworkId, BodyId, BodyPart}; +pub use junction::{BodyId, BodyPart, Junction, NetworkId}; +pub use multi_location::MultiLocation; pub use multiasset::{ - AssetId, AssetInstance, MultiAsset, MultiAssets, MultiAssetFilter, Fungibility, WildMultiAsset, WildFungibility + AssetId, AssetInstance, Fungibility, MultiAsset, MultiAssetFilter, MultiAssets, + WildFungibility, WildMultiAsset, }; -pub use multi_location::MultiLocation; pub use order::Order; -pub use traits::{Error, Result, SendXcm, ExecuteXcm, Outcome}; +pub use traits::{Error, ExecuteXcm, Outcome, Result, SendXcm}; /// A prelude for importing all types typically used when interacting with XCM messages. pub mod prelude { - pub use super::junction::{Junction::*, NetworkId::{self, *}, BodyId, BodyPart}; - pub use super::multiasset::{ - MultiAssets, MultiAsset, - AssetId::{self, *}, - AssetInstance::{self, *}, - MultiAssetFilter::{self, *}, - Fungibility::{self, *}, - WildMultiAsset::{self, *}, - WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, + pub use super::{ + junction::{ + BodyId, BodyPart, + Junction::*, + NetworkId::{self, *}, + }, + multi_location::MultiLocation::{self, *}, + multiasset::{ + AssetId::{self, *}, + AssetInstance::{self, *}, + Fungibility::{self, *}, + MultiAsset, + MultiAssetFilter::{self, *}, + MultiAssets, + WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, + WildMultiAsset::{self, *}, + }, + opaque, + order::Order::{self, *}, + traits::{Error as XcmError, ExecuteXcm, Outcome, Result as XcmResult, SendXcm}, + OriginKind, Response, + Xcm::{self, *}, }; - pub use super::multi_location::MultiLocation::{self, *}; - pub use super::order::Order::{self, *}; - pub use super::traits::{Error as XcmError, Result as XcmResult, SendXcm, ExecuteXcm, Outcome}; - pub use super::{Xcm::{self, *}, OriginKind, Response}; - pub use super::opaque; } // TODO: #2841 #XCMENCODE Efficient encodings for MultiAssets, Vec, using initial byte values 128+ to encode @@ -159,7 +168,11 @@ pub enum Xcm { /// /// Errors: #[codec(index = 3)] - QueryResponse { #[codec(compact)] query_id: u64, response: Response }, + QueryResponse { + #[codec(compact)] + query_id: u64, + response: Response, + }, /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the /// ownership of `dest` within this consensus system. @@ -221,9 +234,12 @@ pub enum Xcm { /// Kind: *System Notification* #[codec(index = 7)] HrmpNewChannelOpenRequest { - #[codec(compact)] sender: u32, - #[codec(compact)] max_message_size: u32, - #[codec(compact)] max_capacity: u32, + #[codec(compact)] + sender: u32, + #[codec(compact)] + max_message_size: u32, + #[codec(compact)] + max_capacity: u32, }, /// A message to notify about that a previously sent open channel request has been accepted by @@ -237,7 +253,8 @@ pub enum Xcm { /// Errors: #[codec(index = 8)] HrmpChannelAccepted { - #[codec(compact)] recipient: u32, + #[codec(compact)] + recipient: u32, }, /// A message to notify that the other party in an open channel decided to close it. In particular, @@ -252,9 +269,12 @@ pub enum Xcm { /// Errors: #[codec(index = 9)] HrmpChannelClosing { - #[codec(compact)] initiator: u32, - #[codec(compact)] sender: u32, - #[codec(compact)] recipient: u32, + #[codec(compact)] + initiator: u32, + #[codec(compact)] + sender: u32, + #[codec(compact)] + recipient: u32, }, /// A message to indicate that the embedded XCM is actually arriving on behalf of some consensus @@ -267,10 +287,7 @@ pub enum Xcm { /// /// Errors: #[codec(index = 10)] - RelayedFrom { - who: MultiLocation, - message: alloc::boxed::Box>, - }, + RelayedFrom { who: MultiLocation, message: alloc::boxed::Box> }, } impl From> for VersionedXcm { @@ -289,32 +306,33 @@ impl TryFrom> for Xcm { } impl Xcm { - pub fn into(self) -> Xcm { Xcm::from(self) } + pub fn into(self) -> Xcm { + Xcm::from(self) + } pub fn from(xcm: Xcm) -> Self { use Xcm::*; match xcm { - WithdrawAsset { assets, effects } - => WithdrawAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, - ReserveAssetDeposited { assets, effects } - => ReserveAssetDeposited { assets, effects: effects.into_iter().map(Order::into).collect() }, - TeleportAsset { assets, effects } - => TeleportAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, - QueryResponse { query_id: u64, response } - => QueryResponse { query_id: u64, response }, - TransferAsset { assets, dest } - => TransferAsset { assets, dest }, - TransferReserveAsset { assets, dest, effects } - => TransferReserveAsset { assets, dest, effects }, - HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity} - => HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity}, - HrmpChannelAccepted { recipient} - => HrmpChannelAccepted { recipient}, - HrmpChannelClosing { initiator, sender, recipient} - => HrmpChannelClosing { initiator, sender, recipient}, - Transact { origin_type, require_weight_at_most, call} - => Transact { origin_type, require_weight_at_most, call: call.into() }, - RelayedFrom { who, message } - => RelayedFrom { who, message: alloc::boxed::Box::new((*message).into()) }, + WithdrawAsset { assets, effects } => + WithdrawAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, + ReserveAssetDeposited { assets, effects } => ReserveAssetDeposited { + assets, + effects: effects.into_iter().map(Order::into).collect(), + }, + TeleportAsset { assets, effects } => + TeleportAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, + QueryResponse { query_id: u64, response } => QueryResponse { query_id: u64, response }, + TransferAsset { assets, dest } => TransferAsset { assets, dest }, + TransferReserveAsset { assets, dest, effects } => + TransferReserveAsset { assets, dest, effects }, + HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => + HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, + HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient }, + HrmpChannelClosing { initiator, sender, recipient } => + HrmpChannelClosing { initiator, sender, recipient }, + Transact { origin_type, require_weight_at_most, call } => + Transact { origin_type, require_weight_at_most, call: call.into() }, + RelayedFrom { who, message } => + RelayedFrom { who, message: alloc::boxed::Box::new((*message).into()) }, } } } diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v0/multiasset.rs index 02c43fc1d205..c11110ed571c 100644 --- a/xcm/src/v0/multiasset.rs +++ b/xcm/src/v0/multiasset.rs @@ -1,5 +1,5 @@ // Copyright 2020 Parity Technologies (UK) Ltd. -// This file is part of Cumulus. +// This file is part of Polkadot. // Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -23,10 +23,10 @@ //! - `MultiAssetFilter`: A combination of `Wild` and `MultiAssets` designed for efficiently filtering an XCM holding //! account. -use core::cmp::Ordering; -use alloc::{vec, vec::Vec}; -use parity_scale_codec::{self as codec, Encode, Decode}; use super::MultiLocation; +use alloc::{vec, vec::Vec}; +use core::cmp::Ordering; +use parity_scale_codec::{self as codec, Decode, Encode}; /// A general identifier for an instance of a non-fungible asset class. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)] @@ -36,7 +36,10 @@ pub enum AssetInstance { /// A compact index. Technically this could be greater than `u128`, but this implementation supports only /// values up to `2**128 - 1`. - Index { #[codec(compact)] id: u128 }, + Index { + #[codec(compact)] + id: u128, + }, /// A 4-byte fixed-length datum. Array4([u8; 4]), @@ -103,7 +106,8 @@ pub enum Fungibility { impl Fungibility { pub fn is_kind(&self, w: WildFungibility) -> bool { - use {Fungibility::*, WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible}}; + use Fungibility::*; + use WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible}; matches!((self, w), (Fungible(_), WildFungible) | (NonFungible(_), WildNonFungible)) } } @@ -121,9 +125,6 @@ impl From for Fungibility { } } - - - #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode)] pub struct MultiAsset { pub id: AssetId, @@ -182,8 +183,6 @@ impl MultiAsset { } } - - /// A `Vec` of `MultiAsset`s. There may be no duplicate fungible items in here and when decoding, they must be sorted. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode)] pub struct MultiAssets(Vec); @@ -191,14 +190,19 @@ pub struct MultiAssets(Vec); impl Decode for MultiAssets { fn decode(input: &mut I) -> Result { let r = Vec::::decode(input)?; - if r.is_empty() { return Ok(Self(Vec::new())) } - r.iter().skip(1).try_fold(&r[0], |a, b| -> Result<&MultiAsset, parity_scale_codec::Error> { - if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) { - Ok(b) - } else { - Err("Out of order".into()) - } - })?; + if r.is_empty() { + return Ok(Self(Vec::new())) + } + r.iter().skip(1).try_fold( + &r[0], + |a, b| -> Result<&MultiAsset, parity_scale_codec::Error> { + if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) { + Ok(b) + } else { + Err("Out of order".into()) + } + }, + )?; Ok(Self(r)) } } @@ -306,8 +310,9 @@ impl, B: Into> From<(A, B)> for WildMultiAsset WildMultiAsset::AllOf { fun: fun.into(), id: id.into() } } } + /// `MultiAsset` collection, either `MultiAssets` or a single wildcard. -/// +/// /// Note: Vectors of wildcards whose encoding is supported in XCM v0 are unsupported /// in this implementation and will result in a decode error. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)] @@ -341,21 +346,6 @@ impl From for MultiAssetFilter { } impl MultiAssetFilter { - /// Returns `true` if the `MultiAsset` is a wildcard and refers to sets of assets, instead of just one. - pub fn is_wildcard(&self) -> bool { - matches!(self, MultiAssetFilter::Wild(..)) - } - - /// Returns `true` if the `MultiAsset` is not a wildcard. - pub fn is_definite(&self) -> bool { - !self.is_wildcard() - } - - /// Returns `true` if this definitely represents no asset. - pub fn is_none(&self) -> bool { - matches!(self, MultiAssetFilter::Definite(a) if a.is_none()) - } - /// 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. diff --git a/xcm/src/v0/order.rs b/xcm/src/v0/order.rs index 3917e6a01116..aed5a6ff3634 100644 --- a/xcm/src/v0/order.rs +++ b/xcm/src/v0/order.rs @@ -16,14 +16,14 @@ //! Version 0 of the Cross-Consensus Message format data structures. +use super::{MultiAsset, MultiAssetFilter, MultiLocation, Xcm}; use alloc::vec::Vec; use derivative::Derivative; -use parity_scale_codec::{self, Encode, Decode}; -use super::{MultiAsset, MultiAssetFilter, MultiLocation, Xcm}; +use parity_scale_codec::{self, Decode, Encode}; /// An instruction to be executed on some or all of the assets in holding, used by asset-related XCM messages. #[derive(Derivative, Encode, Decode)] -#[derivative(Clone(bound=""), Eq(bound=""), PartialEq(bound=""), Debug(bound=""))] +#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] #[codec(encode_bound())] #[codec(decode_bound())] pub enum Order { @@ -78,7 +78,11 @@ pub enum Order { /// /// Errors: #[codec(index = 4)] - InitiateReserveWithdraw { assets: MultiAssetFilter, reserve: MultiLocation, effects: Vec> }, + InitiateReserveWithdraw { + assets: MultiAssetFilter, + reserve: MultiLocation, + effects: Vec>, + }, /// Remove the asset(s) (`assets`) from holding and send a `TeleportAsset` XCM message to a destination location. /// @@ -100,7 +104,12 @@ pub enum Order { /// /// Errors: #[codec(index = 6)] - QueryHolding { #[codec(compact)] query_id: u64, dest: MultiLocation, assets: MultiAssetFilter }, + QueryHolding { + #[codec(compact)] + query_id: u64, + dest: MultiLocation, + assets: MultiAssetFilter, + }, /// Pay for the execution of some XCM with up to `weight` picoseconds of execution time, paying for this with /// up to `fees` from the holding register. @@ -109,7 +118,13 @@ pub enum Order { /// /// Errors: #[codec(index = 7)] - BuyExecution { fees: MultiAsset, weight: u64, debt: u64, halt_on_error: bool, xcm: Vec> }, + BuyExecution { + fees: MultiAsset, + weight: u64, + debt: u64, + halt_on_error: bool, + xcm: Vec>, + }, } pub mod opaque { @@ -117,23 +132,22 @@ pub mod opaque { } impl Order { - pub fn into(self) -> Order { Order::from(self) } + pub fn into(self) -> Order { + Order::from(self) + } pub fn from(order: Order) -> Self { use Order::*; match order { Noop => Noop, - DepositAsset { assets, dest } - => DepositAsset { assets, dest }, - DepositReserveAsset { assets, dest, effects } - => DepositReserveAsset { assets, dest, effects }, - ExchangeAsset { give, receive } - => ExchangeAsset { give, receive }, - InitiateReserveWithdraw { assets, reserve, effects } - => InitiateReserveWithdraw { assets, reserve, effects }, - InitiateTeleport { assets, dest, effects } - => InitiateTeleport { assets, dest, effects }, - QueryHolding { query_id, dest, assets } - => QueryHolding { query_id, dest, assets }, + DepositAsset { assets, dest } => DepositAsset { assets, dest }, + DepositReserveAsset { assets, dest, effects } => + DepositReserveAsset { assets, dest, effects }, + ExchangeAsset { give, receive } => ExchangeAsset { give, receive }, + InitiateReserveWithdraw { assets, reserve, effects } => + InitiateReserveWithdraw { assets, reserve, effects }, + InitiateTeleport { assets, dest, effects } => + InitiateTeleport { assets, dest, effects }, + QueryHolding { query_id, dest, assets } => QueryHolding { query_id, dest, assets }, BuyExecution { fees, weight, debt, halt_on_error, xcm } => { let xcm = xcm.into_iter().map(Xcm::from).collect(); BuyExecution { fees, weight, debt, halt_on_error, xcm } diff --git a/xcm/xcm-builder/src/barriers.rs b/xcm/xcm-builder/src/barriers.rs index 0a48ad6c42e2..2d870d7525c2 100644 --- a/xcm/xcm-builder/src/barriers.rs +++ b/xcm/xcm-builder/src/barriers.rs @@ -16,11 +16,11 @@ //! Various implementations for `ShouldExecute`. -use sp_std::{result::Result, marker::PhantomData}; -use xcm::v0::{Xcm, Order, MultiLocation, Junction}; use frame_support::{ensure, traits::Contains, weights::Weight}; -use xcm_executor::traits::{OnResponse, ShouldExecute}; use polkadot_parachain::primitives::IsSystem; +use sp_std::{marker::PhantomData, result::Result}; +use xcm::v0::{Junction, MultiLocation, Order, Xcm}; +use xcm_executor::traits::{OnResponse, ShouldExecute}; /// Execution barrier that just takes `shallow_weight` from `weight_credit`. pub struct TakeWeightCredit; @@ -51,14 +51,14 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFro ensure!(T::contains(origin), ()); ensure!(top_level, ()); match message { - Xcm::TeleportAsset { effects, .. } - | Xcm::WithdrawAsset { effects, ..} - | Xcm::ReserveAssetDeposited { effects, ..} - if matches!( - effects.first(), - Some(Order::BuyExecution { debt, ..}) if *debt >= shallow_weight - ) - => Ok(()), + Xcm::TeleportAsset { effects, .. } | + Xcm::WithdrawAsset { effects, .. } | + Xcm::ReserveAssetDeposited { effects, .. } + if matches!( + effects.first(), + Some(Order::BuyExecution { debt, ..}) if *debt >= shallow_weight + ) => + Ok(()), _ => Err(()), } } @@ -82,9 +82,7 @@ impl> ShouldExecute for AllowUnpaidExecutionFrom { /// Allows a message only if it is from a system-level child parachain. pub struct IsChildSystemParachain(PhantomData); -impl< - ParaId: IsSystem + From, -> Contains for IsChildSystemParachain { +impl> Contains for IsChildSystemParachain { fn contains(l: &MultiLocation) -> bool { matches!(l, MultiLocation::X1(Junction::Parachain(id)) if ParaId::from(*id).is_system()) } @@ -101,8 +99,9 @@ impl ShouldExecute for AllowKnownQueryResponses Result<(), ()> { match message { - Xcm::QueryResponse { query_id, .. } if ResponseHandler::expecting_response(origin, *query_id) - => Ok(()), + Xcm::QueryResponse { query_id, .. } + if ResponseHandler::expecting_response(origin, *query_id) => + Ok(()), _ => Err(()), } } diff --git a/xcm/xcm-builder/src/filter_asset_location.rs b/xcm/xcm-builder/src/filter_asset_location.rs index 462b9421e35a..cb2fa80be734 100644 --- a/xcm/xcm-builder/src/filter_asset_location.rs +++ b/xcm/xcm-builder/src/filter_asset_location.rs @@ -16,9 +16,9 @@ //! Various implementations of `FilterAssetLocation`. -use sp_std::marker::PhantomData; -use xcm::v0::{MultiAsset, MultiAssetFilter, MultiLocation, AssetId::Concrete}; use frame_support::traits::Get; +use sp_std::marker::PhantomData; +use xcm::v0::{AssetId::Concrete, MultiAsset, MultiAssetFilter, MultiLocation}; use xcm_executor::traits::FilterAssetLocation; /// Accepts an asset iff it is a native asset. diff --git a/xcm/xcm-builder/src/fungibles_adapter.rs b/xcm/xcm-builder/src/fungibles_adapter.rs index 121d0828f165..d81ddd5a575d 100644 --- a/xcm/xcm-builder/src/fungibles_adapter.rs +++ b/xcm/xcm-builder/src/fungibles_adapter.rs @@ -16,23 +16,25 @@ //! Adapters to work with `frame_support::traits::tokens::fungibles` through XCM. -use sp_std::{prelude::*, result, marker::PhantomData, borrow::Borrow}; +use frame_support::traits::{tokens::fungibles, Contains, Get}; +use sp_std::{borrow::Borrow, marker::PhantomData, prelude::*, result}; use xcm::v0::{ - Error as XcmError, Result, MultiAsset, MultiLocation, Junction, Fungibility::Fungible, - AssetId::{Concrete, Abstract}, + AssetId::{Abstract, Concrete}, + Error as XcmError, + Fungibility::Fungible, + Junction, MultiAsset, MultiLocation, Result, }; -use frame_support::traits::{Get, tokens::fungibles, Contains}; -use xcm_executor::traits::{TransactAsset, Convert, MatchesFungibles, Error as MatchError}; +use xcm_executor::traits::{Convert, Error as MatchError, MatchesFungibles, TransactAsset}; /// Converter struct implementing `AssetIdConversion` converting a numeric asset ID (must be `TryFrom/TryInto`) into /// a `GeneralIndex` junction, prefixed by some `MultiLocation` value. The `MultiLocation` value will typically be a /// `PalletInstance` junction. -pub struct AsPrefixedGeneralIndex(PhantomData<(Prefix, AssetId, ConvertAssetId)>); -impl< - Prefix: Get, - AssetId: Clone, - ConvertAssetId: Convert, -> Convert for AsPrefixedGeneralIndex { +pub struct AsPrefixedGeneralIndex( + PhantomData<(Prefix, AssetId, ConvertAssetId)>, +); +impl, AssetId: Clone, ConvertAssetId: Convert> + Convert for AsPrefixedGeneralIndex +{ fn convert_ref(id: impl Borrow) -> result::Result { let prefix = Prefix::get(); let id = id.borrow(); @@ -53,58 +55,63 @@ impl< } pub struct ConvertedConcreteAssetId( - PhantomData<(AssetId, Balance, ConvertAssetId, ConvertBalance)> + PhantomData<(AssetId, Balance, ConvertAssetId, ConvertBalance)>, ); impl< - AssetId: Clone, - Balance: Clone, - ConvertAssetId: Convert, - ConvertBalance: Convert, -> MatchesFungibles for - ConvertedConcreteAssetId + AssetId: Clone, + Balance: Clone, + ConvertAssetId: Convert, + ConvertBalance: Convert, + > MatchesFungibles + for ConvertedConcreteAssetId { fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> { let (amount, id) = match (&a.fun, &a.id) { (Fungible(ref amount), Concrete(ref id)) => (amount, id), _ => return Err(MatchError::AssetNotFound), }; - let what = ConvertAssetId::convert_ref(id).map_err(|_| MatchError::AssetIdConversionFailed)?; - let amount = ConvertBalance::convert_ref(amount).map_err(|_| MatchError::AmountToBalanceConversionFailed)?; + let what = + ConvertAssetId::convert_ref(id).map_err(|_| MatchError::AssetIdConversionFailed)?; + let amount = ConvertBalance::convert_ref(amount) + .map_err(|_| MatchError::AmountToBalanceConversionFailed)?; Ok((what, amount)) } } pub struct ConvertedAbstractAssetId( - PhantomData<(AssetId, Balance, ConvertAssetId, ConvertBalance)> + PhantomData<(AssetId, Balance, ConvertAssetId, ConvertBalance)>, ); impl< - AssetId: Clone, - Balance: Clone, - ConvertAssetId: Convert, AssetId>, - ConvertBalance: Convert, -> MatchesFungibles for - ConvertedAbstractAssetId + AssetId: Clone, + Balance: Clone, + ConvertAssetId: Convert, AssetId>, + ConvertBalance: Convert, + > MatchesFungibles + for ConvertedAbstractAssetId { fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> { let (amount, id) = match (&a.fun, &a.id) { (Fungible(ref amount), Abstract(ref id)) => (amount, id), _ => return Err(MatchError::AssetNotFound), }; - let what = ConvertAssetId::convert_ref(id).map_err(|_| MatchError::AssetIdConversionFailed)?; - let amount = ConvertBalance::convert_ref(amount).map_err(|_| MatchError::AmountToBalanceConversionFailed)?; + let what = + ConvertAssetId::convert_ref(id).map_err(|_| MatchError::AssetIdConversionFailed)?; + let amount = ConvertBalance::convert_ref(amount) + .map_err(|_| MatchError::AmountToBalanceConversionFailed)?; Ok((what, amount)) } } pub struct FungiblesTransferAdapter( - PhantomData<(Assets, Matcher, AccountIdConverter, AccountId)> + PhantomData<(Assets, Matcher, AccountIdConverter, AccountId)>, ); impl< - Assets: fungibles::Transfer, - Matcher: MatchesFungibles, - AccountIdConverter: Convert, - AccountId: Clone, // can't get away without it since Currency is generic over it. -> TransactAsset for FungiblesTransferAdapter { + Assets: fungibles::Transfer, + Matcher: MatchesFungibles, + AccountIdConverter: Convert, + AccountId: Clone, // can't get away without it since Currency is generic over it. + > TransactAsset for FungiblesTransferAdapter +{ fn transfer_asset( what: &MultiAsset, from: &MultiLocation, @@ -122,17 +129,31 @@ impl< } } -pub struct FungiblesMutateAdapter( - PhantomData<(Assets, Matcher, AccountIdConverter, AccountId, CheckAsset, CheckingAccount)> -); +pub struct FungiblesMutateAdapter< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, +>(PhantomData<(Assets, Matcher, AccountIdConverter, AccountId, CheckAsset, CheckingAccount)>); impl< - Assets: fungibles::Mutate, - Matcher: MatchesFungibles, - AccountIdConverter: Convert, - AccountId: Clone, // can't get away without it since Currency is generic over it. - CheckAsset: Contains, - CheckingAccount: Get, -> TransactAsset for FungiblesMutateAdapter { + Assets: fungibles::Mutate, + Matcher: MatchesFungibles, + AccountIdConverter: Convert, + AccountId: Clone, // can't get away without it since Currency is generic over it. + CheckAsset: Contains, + CheckingAccount: Get, + > TransactAsset + for FungiblesMutateAdapter< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + > +{ fn can_check_in(_origin: &MultiLocation, what: &MultiAsset) -> Result { // Check we handle this asset. let (asset_id, amount) = Matcher::matches_fungibles(what)?; @@ -151,7 +172,10 @@ impl< if CheckAsset::contains(&asset_id) { let checking_account = CheckingAccount::get(); let ok = Assets::burn_from(asset_id, &checking_account, amount).is_ok(); - debug_assert!(ok, "`can_check_in` must have returned `true` immediately prior; qed"); + debug_assert!( + ok, + "`can_check_in` must have returned `true` immediately prior; qed" + ); } } } @@ -177,7 +201,7 @@ impl< fn withdraw_asset( what: &MultiAsset, - who: &MultiLocation + who: &MultiLocation, ) -> result::Result { // Check we handle this asset. let (asset_id, amount) = Matcher::matches_fungibles(what)?; @@ -189,43 +213,80 @@ impl< } } -pub struct FungiblesAdapter( - PhantomData<(Assets, Matcher, AccountIdConverter, AccountId, CheckAsset, CheckingAccount)> -); +pub struct FungiblesAdapter< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, +>(PhantomData<(Assets, Matcher, AccountIdConverter, AccountId, CheckAsset, CheckingAccount)>); impl< - Assets: fungibles::Mutate + fungibles::Transfer, - Matcher: MatchesFungibles, - AccountIdConverter: Convert, - AccountId: Clone, // can't get away without it since Currency is generic over it. - CheckAsset: Contains, - CheckingAccount: Get, -> TransactAsset for FungiblesAdapter { + Assets: fungibles::Mutate + fungibles::Transfer, + Matcher: MatchesFungibles, + AccountIdConverter: Convert, + AccountId: Clone, // can't get away without it since Currency is generic over it. + CheckAsset: Contains, + CheckingAccount: Get, + > TransactAsset + for FungiblesAdapter +{ fn can_check_in(origin: &MultiLocation, what: &MultiAsset) -> Result { - FungiblesMutateAdapter:: - ::can_check_in(origin, what) + FungiblesMutateAdapter::< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + >::can_check_in(origin, what) } fn check_in(origin: &MultiLocation, what: &MultiAsset) { - FungiblesMutateAdapter:: - ::check_in(origin, what) + FungiblesMutateAdapter::< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + >::check_in(origin, what) } fn check_out(dest: &MultiLocation, what: &MultiAsset) { - FungiblesMutateAdapter:: - ::check_out(dest, what) + FungiblesMutateAdapter::< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + >::check_out(dest, what) } fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> Result { - FungiblesMutateAdapter:: - ::deposit_asset(what, who) + FungiblesMutateAdapter::< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + >::deposit_asset(what, who) } fn withdraw_asset( what: &MultiAsset, - who: &MultiLocation + who: &MultiLocation, ) -> result::Result { - FungiblesMutateAdapter:: - ::withdraw_asset(what, who) + FungiblesMutateAdapter::< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + >::withdraw_asset(what, who) } fn transfer_asset( @@ -233,6 +294,8 @@ impl< from: &MultiLocation, to: &MultiLocation, ) -> result::Result { - FungiblesTransferAdapter::::transfer_asset(what, from, to) + FungiblesTransferAdapter::::transfer_asset( + what, from, to, + ) } } diff --git a/xcm/xcm-builder/src/lib.rs b/xcm/xcm-builder/src/lib.rs index 339a9321e1ad..d2e2d2e23f38 100644 --- a/xcm/xcm-builder/src/lib.rs +++ b/xcm/xcm-builder/src/lib.rs @@ -27,21 +27,22 @@ mod tests; mod location_conversion; pub use location_conversion::{ - Account32Hash, ParentIsDefault, ChildParachainConvertsVia, SiblingParachainConvertsVia, AccountId32Aliases, - AccountKey20Aliases, LocationInverter, + Account32Hash, AccountId32Aliases, AccountKey20Aliases, ChildParachainConvertsVia, + LocationInverter, ParentIsDefault, SiblingParachainConvertsVia, }; mod origin_conversion; pub use origin_conversion::{ - SovereignSignedViaLocation, ParentAsSuperuser, ChildSystemParachainAsSuperuser, SiblingSystemParachainAsSuperuser, - ChildParachainAsNative, SiblingParachainAsNative, RelayChainAsNative, SignedAccountId32AsNative, - SignedAccountKey20AsNative, EnsureXcmOrigin, SignedToAccountId32, BackingToPlurality, + BackingToPlurality, ChildParachainAsNative, ChildSystemParachainAsSuperuser, EnsureXcmOrigin, + ParentAsSuperuser, RelayChainAsNative, SiblingParachainAsNative, + SiblingSystemParachainAsSuperuser, SignedAccountId32AsNative, SignedAccountKey20AsNative, + SignedToAccountId32, SovereignSignedViaLocation, }; mod barriers; pub use barriers::{ - TakeWeightCredit, AllowUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, AllowKnownQueryResponses, - IsChildSystemParachain, + AllowKnownQueryResponses, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, + IsChildSystemParachain, TakeWeightCredit, }; mod currency_adapter; @@ -50,13 +51,13 @@ pub use currency_adapter::CurrencyAdapter; mod fungibles_adapter; pub use fungibles_adapter::{ AsPrefixedGeneralIndex, ConvertedAbstractAssetId, ConvertedConcreteAssetId, FungiblesAdapter, - FungiblesMutateAdapter, FungiblesTransferAdapter + FungiblesMutateAdapter, FungiblesTransferAdapter, }; mod weight; -pub use weight::{FixedRateOfFungible, FixedWeightBounds, UsingComponents, TakeRevenue}; #[allow(deprecated)] pub use weight::FixedRateOfConcreteFungible; +pub use weight::{FixedRateOfFungible, FixedWeightBounds, TakeRevenue, UsingComponents}; mod matches_fungible; pub use matches_fungible::{IsAbstract, IsConcrete}; diff --git a/xcm/xcm-builder/src/matches_fungible.rs b/xcm/xcm-builder/src/matches_fungible.rs index 6b80994d1989..0b635f3c1cb8 100644 --- a/xcm/xcm-builder/src/matches_fungible.rs +++ b/xcm/xcm-builder/src/matches_fungible.rs @@ -16,10 +16,14 @@ //! Various implementations for the `MatchesFungible` trait. -use sp_std::{marker::PhantomData, convert::TryFrom}; -use sp_runtime::traits::CheckedConversion; -use xcm::v0::{MultiAsset, MultiLocation, AssetId::{Abstract, Concrete}, Fungibility::Fungible}; use frame_support::traits::Get; +use sp_runtime::traits::CheckedConversion; +use sp_std::{convert::TryFrom, marker::PhantomData}; +use xcm::v0::{ + AssetId::{Abstract, Concrete}, + Fungibility::Fungible, + MultiAsset, MultiLocation, +}; use xcm_executor::traits::MatchesFungible; /// Converts a `MultiAsset` into balance `B` if it is a concrete fungible with an id equal to that diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index d036d2997df1..6adc5b478343 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -14,23 +14,30 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -pub use sp_std::{fmt::Debug, marker::PhantomData, cell::RefCell}; -pub use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; -pub use parity_scale_codec::{Encode, Decode}; -pub use xcm::v0::prelude::*; +pub use crate::{ + AllowKnownQueryResponses, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, + FixedRateOfFungible, FixedWeightBounds, LocationInverter, TakeWeightCredit, +}; pub use frame_support::{ + dispatch::{ + DispatchError, DispatchInfo, DispatchResultWithPostInfo, Dispatchable, Parameter, Weight, + }, ensure, parameter_types, - dispatch::{Dispatchable, Parameter, Weight, DispatchError, DispatchResultWithPostInfo, DispatchInfo}, - weights::{PostDispatchInfo, GetDispatchInfo}, sp_runtime::DispatchErrorWithPostInfo, - traits::{Get, Contains, IsInVec}, + traits::{Contains, Get, IsInVec}, + weights::{GetDispatchInfo, PostDispatchInfo}, }; -pub use xcm_executor::{ - Assets, Config, traits::{TransactAsset, ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse} +pub use parity_scale_codec::{Decode, Encode}; +pub use sp_std::{ + cell::RefCell, + collections::{btree_map::BTreeMap, btree_set::BTreeSet}, + fmt::Debug, + marker::PhantomData, }; -pub use crate::{ - TakeWeightCredit, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, FixedWeightBounds, - FixedRateOfFungible, AllowKnownQueryResponses, LocationInverter, +pub use xcm::v0::prelude::*; +pub use xcm_executor::{ + traits::{ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset}, + Assets, Config, }; pub enum TestOrigin { @@ -59,20 +66,18 @@ impl Dispatchable for TestCall { fn dispatch(self, origin: Self::Origin) -> DispatchResultWithPostInfo { let mut post_info = PostDispatchInfo::default(); post_info.actual_weight = match self { - TestCall::OnlyRoot(_, maybe_actual) - | TestCall::OnlySigned(_, maybe_actual, _) - | TestCall::OnlyParachain(_, maybe_actual, _) - | TestCall::Any(_, maybe_actual) - => maybe_actual, + TestCall::OnlyRoot(_, maybe_actual) | + TestCall::OnlySigned(_, maybe_actual, _) | + TestCall::OnlyParachain(_, maybe_actual, _) | + TestCall::Any(_, maybe_actual) => maybe_actual, }; if match (&origin, &self) { (TestOrigin::Parachain(i), TestCall::OnlyParachain(_, _, Some(j))) => i == j, (TestOrigin::Signed(i), TestCall::OnlySigned(_, _, Some(j))) => i == j, - (TestOrigin::Root, TestCall::OnlyRoot(..)) - | (TestOrigin::Parachain(_), TestCall::OnlyParachain(_, _, None)) - | (TestOrigin::Signed(_), TestCall::OnlySigned(_, _, None)) - | (_, TestCall::Any(..)) - => true, + (TestOrigin::Root, TestCall::OnlyRoot(..)) | + (TestOrigin::Parachain(_), TestCall::OnlyParachain(_, _, None)) | + (TestOrigin::Signed(_), TestCall::OnlySigned(_, _, None)) | + (_, TestCall::Any(..)) => true, _ => false, } { Ok(post_info) @@ -85,13 +90,12 @@ impl Dispatchable for TestCall { impl GetDispatchInfo for TestCall { fn get_dispatch_info(&self) -> DispatchInfo { let weight = *match self { - TestCall::OnlyRoot(estimate, ..) - | TestCall::OnlyParachain(estimate, ..) - | TestCall::OnlySigned(estimate, ..) - | TestCall::Any(estimate, ..) - => estimate, + TestCall::OnlyRoot(estimate, ..) | + TestCall::OnlyParachain(estimate, ..) | + TestCall::OnlySigned(estimate, ..) | + TestCall::Any(estimate, ..) => estimate, }; - DispatchInfo { weight, .. Default::default() } + DispatchInfo { weight, ..Default::default() } } } @@ -116,11 +120,7 @@ pub fn assets(who: u64) -> Vec { ASSETS.with(|a| a.borrow().get(&who).map_or(vec![], |a| a.clone().into())) } pub fn add_asset(who: u64, what: MultiAsset) { - ASSETS.with(|a| a.borrow_mut() - .entry(who) - .or_insert(Assets::new()) - .subsume(what) - ); + ASSETS.with(|a| a.borrow_mut().entry(who).or_insert(Assets::new()).subsume(what)); } pub struct TestAssetTransactor; @@ -133,16 +133,16 @@ impl TransactAsset for TestAssetTransactor { fn withdraw_asset(what: &MultiAsset, who: &MultiLocation) -> Result { let who = to_account(who.clone()).map_err(|_| XcmError::LocationCannotHold)?; - ASSETS.with(|a| a.borrow_mut() - .get_mut(&who) - .ok_or(XcmError::NotWithdrawable)? - .try_take(what.clone().into()) - .map_err(|_| XcmError::NotWithdrawable) - ) + ASSETS.with(|a| { + a.borrow_mut() + .get_mut(&who) + .ok_or(XcmError::NotWithdrawable)? + .try_take(what.clone().into()) + .map_err(|_| XcmError::NotWithdrawable) + }) } } - pub fn to_account(l: MultiLocation) -> Result { Ok(match l { // Siblings at 2000+id @@ -161,14 +161,17 @@ pub fn to_account(l: MultiLocation) -> Result { pub struct TestOriginConverter; impl ConvertOrigin for TestOriginConverter { - fn convert_origin(origin: MultiLocation, kind: OriginKind) -> Result { + fn convert_origin( + origin: MultiLocation, + kind: OriginKind, + ) -> Result { use OriginKind::*; match (kind, origin) { (Superuser, _) => Ok(TestOrigin::Root), (SovereignAccount, l) => Ok(TestOrigin::Signed(to_account(l)?)), (Native, X1(Parachain(id))) => Ok(TestOrigin::Parachain(id)), (Native, X1(Parent)) => Ok(TestOrigin::Relay), - (Native, X1(AccountIndex64 {index, ..})) => Ok(TestOrigin::Signed(index)), + (Native, X1(AccountIndex64 { index, .. })) => Ok(TestOrigin::Signed(index)), (_, origin) => Err(origin), } } @@ -188,17 +191,15 @@ pub fn add_teleporter(from: MultiLocation, asset: MultiAssetFilter) { pub struct TestIsReserve; impl FilterAssetLocation for TestIsReserve { fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { - IS_RESERVE.with(|r| r.borrow().get(origin) - .map_or(false, |v| v.iter().any(|a| a.contains(asset))) - ) + IS_RESERVE + .with(|r| r.borrow().get(origin).map_or(false, |v| v.iter().any(|a| a.contains(asset)))) } } pub struct TestIsTeleporter; impl FilterAssetLocation for TestIsTeleporter { fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { - IS_TELEPORTER.with(|r| r.borrow().get(origin) - .map_or(false, |v| v.iter().any(|a| a.contains(asset))) - ) + IS_TELEPORTER + .with(|r| r.borrow().get(origin).map_or(false, |v| v.iter().any(|a| a.contains(asset)))) } } @@ -220,28 +221,25 @@ impl OnResponse for TestResponseHandler { } fn on_response(_origin: MultiLocation, query_id: u64, response: xcm::v0::Response) -> Weight { QUERIES.with(|q| { - q.borrow_mut() - .entry(query_id) - .and_modify(|v| if matches!(*v, ResponseSlot::Expecting(..)) { + q.borrow_mut().entry(query_id).and_modify(|v| { + if matches!(*v, ResponseSlot::Expecting(..)) { *v = ResponseSlot::Received(response); - }); + } + }); }); 10 } } pub fn expect_response(query_id: u64, from: MultiLocation) { - QUERIES.with(|q| q.borrow_mut() - .insert(query_id, ResponseSlot::Expecting(from)) - ); + QUERIES.with(|q| q.borrow_mut().insert(query_id, ResponseSlot::Expecting(from))); } pub fn response(query_id: u64) -> Option { - QUERIES.with(|q| q.borrow() - .get(&query_id) - .and_then(|v| match v { + QUERIES.with(|q| { + q.borrow().get(&query_id).and_then(|v| match v { ResponseSlot::Received(r) => Some(r.clone()), _ => None, }) - ) + }) } parameter_types! { diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index e23c262ea660..5c7465440b29 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -14,10 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use super::*; -use super::mock::*; +use super::{mock::*, *}; use xcm::v0::prelude::*; -use xcm_executor::{XcmExecutor, Config, traits::*}; +use xcm_executor::{traits::*, Config, XcmExecutor}; #[test] fn basic_setup_works() { @@ -31,8 +30,8 @@ fn basic_setup_works() { assert_eq!(to_account(X1(Parachain(50))), Ok(1050)); assert_eq!(to_account(X2(Parent, Parachain(1))), Ok(2001)); assert_eq!(to_account(X2(Parent, Parachain(50))), Ok(2050)); - assert_eq!(to_account(X1(AccountIndex64{index:1, network:Any})), Ok(1)); - assert_eq!(to_account(X1(AccountIndex64{index:42, network:Any})), Ok(42)); + assert_eq!(to_account(X1(AccountIndex64 { index: 1, network: Any })), Ok(1)); + assert_eq!(to_account(X1(AccountIndex64 { index: 42, network: Any })), Ok(42)); assert_eq!(to_account(Null), Ok(3000)); } @@ -41,50 +40,41 @@ fn weigher_should_work() { let mut message = opaque::Xcm::ReserveAssetDeposited { assets: (X1(Parent), 100).into(), effects: vec![ - Order::BuyExecution { fees: (X1(Parent), 1).into(), weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, + Order::BuyExecution { + fees: (X1(Parent), 1).into(), + weight: 0, + debt: 30, + halt_on_error: true, + xcm: vec![], + }, Order::DepositAsset { assets: All.into(), dest: Null }, ], - }.into(); + } + .into(); assert_eq!(::Weigher::shallow(&mut message), Ok(30)); } #[test] fn take_weight_credit_barrier_should_work() { - let mut message = opaque::Xcm::TransferAsset { - assets: (X1(Parent), 100).into(), - dest: Null, - }; + let mut message = opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), dest: Null }; let mut weight_credit = 10; - let r = TakeWeightCredit::should_execute( - &X1(Parent), - true, - &mut message, - 10, - &mut weight_credit, - ); + let r = + TakeWeightCredit::should_execute(&X1(Parent), true, &mut message, 10, &mut weight_credit); assert_eq!(r, Ok(())); assert_eq!(weight_credit, 0); - let r = TakeWeightCredit::should_execute( - &X1(Parent), - true, - &mut message, - 10, - &mut weight_credit, - ); + let r = + TakeWeightCredit::should_execute(&X1(Parent), true, &mut message, 10, &mut weight_credit); assert_eq!(r, Err(())); assert_eq!(weight_credit, 0); } #[test] fn allow_unpaid_should_work() { - let mut message = opaque::Xcm::TransferAsset { - assets: (X1(Parent), 100).into(), - dest: Null, - }; + let mut message = opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), dest: Null }; - AllowUnpaidFrom::set(vec![ X1(Parent) ]); + AllowUnpaidFrom::set(vec![X1(Parent)]); let r = AllowUnpaidExecutionFrom::>::should_execute( &X1(Parachain(1)), @@ -107,12 +97,9 @@ fn allow_unpaid_should_work() { #[test] fn allow_paid_should_work() { - AllowPaidFrom::set(vec![ X1(Parent) ]); + AllowPaidFrom::set(vec![X1(Parent)]); - let mut message = opaque::Xcm::TransferAsset { - assets: (X1(Parent), 100).into(), - dest: Null, - }; + let mut message = opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), dest: Null }; let r = AllowTopLevelPaidExecutionFrom::>::should_execute( &X1(Parachain(1)), @@ -171,7 +158,7 @@ fn allow_paid_should_work() { #[test] fn paying_reserve_deposit_should_work() { - AllowPaidFrom::set(vec![ X1(Parent) ]); + AllowPaidFrom::set(vec![X1(Parent)]); add_reserve(X1(Parent), (X1(Parent), WildFungible).into()); WeightPrice::set((X1(Parent).into(), 1_000_000_000_000)); @@ -180,7 +167,13 @@ fn paying_reserve_deposit_should_work() { let message = Xcm::::ReserveAssetDeposited { assets: (X1(Parent), 100).into(), effects: vec![ - Order::::BuyExecution { fees, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, + Order::::BuyExecution { + fees, + weight: 0, + debt: 30, + halt_on_error: true, + xcm: vec![], + }, Order::::DepositAsset { assets: All.into(), dest: Null }, ], }; @@ -193,7 +186,7 @@ fn paying_reserve_deposit_should_work() { #[test] fn transfer_should_work() { // we'll let them have message execution for free. - AllowUnpaidFrom::set(vec![ X1(Parachain(1)) ]); + AllowUnpaidFrom::set(vec![X1(Parachain(1))]); // Child parachain #1 owns 1000 tokens held by us in reserve. add_asset(1001, (Null, 1000).into()); // They want to transfer 100 of them to their sibling parachain #2 @@ -201,7 +194,7 @@ fn transfer_should_work() { X1(Parachain(1)), Xcm::TransferAsset { assets: (Null, 100).into(), - dest: X1(AccountIndex64{index:3, network:Any}), + dest: X1(AccountIndex64 { index: 3, network: Any }), }, 50, ); @@ -213,11 +206,11 @@ fn transfer_should_work() { #[test] fn reserve_transfer_should_work() { - AllowUnpaidFrom::set(vec![ X1(Parachain(1)) ]); + AllowUnpaidFrom::set(vec![X1(Parachain(1))]); // Child parachain #1 owns 1000 tokens held by us in reserve. add_asset(1001, (Null, 1000).into()); // The remote account owned by gav. - let three = X1(AccountIndex64{index:3, network:Any}); + let three = X1(AccountIndex64 { index: 3, network: Any }); // They want to transfer 100 of our native asset from sovereign account of parachain #1 into #2 // and let them know to hand it to account #3. @@ -226,25 +219,28 @@ fn reserve_transfer_should_work() { Xcm::TransferReserveAsset { assets: (Null, 100).into(), dest: X1(Parachain(2)), - effects: vec![ Order::DepositAsset { assets: All.into(), dest: three.clone() } ], + effects: vec![Order::DepositAsset { assets: All.into(), dest: three.clone() }], }, 50, ); assert_eq!(r, Outcome::Complete(10)); assert_eq!(assets(1002), vec![(Null, 100).into()]); - assert_eq!(sent_xcm(), vec![( - X1(Parachain(2)), - Xcm::ReserveAssetDeposited { - assets: (X1(Parent), 100).into(), - effects: vec![ Order::DepositAsset { assets: All.into(), dest: three } ], - }) - ]); + assert_eq!( + sent_xcm(), + vec![( + X1(Parachain(2)), + Xcm::ReserveAssetDeposited { + assets: (X1(Parent), 100).into(), + effects: vec![Order::DepositAsset { assets: All.into(), dest: three }], + } + )] + ); } #[test] fn transacting_should_work() { - AllowUnpaidFrom::set(vec![ X1(Parent) ]); + AllowUnpaidFrom::set(vec![X1(Parent)]); let origin = X1(Parent); let message = Xcm::::Transact { @@ -259,7 +255,7 @@ fn transacting_should_work() { #[test] fn transacting_should_respect_max_weight_requirement() { - AllowUnpaidFrom::set(vec![ X1(Parent) ]); + AllowUnpaidFrom::set(vec![X1(Parent)]); let origin = X1(Parent); let message = Xcm::::Transact { @@ -274,7 +270,7 @@ fn transacting_should_respect_max_weight_requirement() { #[test] fn transacting_should_refund_weight() { - AllowUnpaidFrom::set(vec![ X1(Parent) ]); + AllowUnpaidFrom::set(vec![X1(Parent)]); let origin = X1(Parent); let message = Xcm::::Transact { @@ -289,24 +285,28 @@ fn transacting_should_refund_weight() { #[test] fn paid_transacting_should_refund_payment_for_unused_weight() { - let one = X1(AccountIndex64{index:1, network:Any}); - AllowPaidFrom::set(vec![ one.clone() ]); + let one = X1(AccountIndex64 { index: 1, network: Any }); + AllowPaidFrom::set(vec![one.clone()]); add_asset(1, (X1(Parent), 100).into()); WeightPrice::set((X1(Parent).into(), 1_000_000_000_000)); let origin = one.clone(); let fees = (X1(Parent), 100).into(); let message = Xcm::::WithdrawAsset { - assets: (X1(Parent), 100).into(), // enough for 100 units of weight. + assets: (X1(Parent), 100).into(), // enough for 100 units of weight. effects: vec![ - Order::::BuyExecution { fees, weight: 70, debt: 30, halt_on_error: true, xcm: vec![ - Xcm::::Transact { + Order::::BuyExecution { + fees, + weight: 70, + debt: 30, + halt_on_error: true, + xcm: vec![Xcm::::Transact { origin_type: OriginKind::Native, require_weight_at_most: 60, // call estimated at 70 but only takes 10. call: TestCall::Any(60, Some(10)).encode().into(), - } - ] }, + }], + }, Order::::DepositAsset { assets: All.into(), dest: one.clone() }, ], }; @@ -324,10 +324,7 @@ fn prepaid_result_of_query_should_get_free_execution() { expect_response(query_id, origin.clone()); let the_response = Response::Assets((X1(Parent), 100).into()); - let message = Xcm::::QueryResponse { - query_id, - response: the_response.clone(), - }; + let message = Xcm::::QueryResponse { query_id, response: the_response.clone() }; let weight_limit = 10; // First time the response gets through since we're expecting it... diff --git a/xcm/xcm-builder/src/weight.rs b/xcm/xcm-builder/src/weight.rs index 500db2a393c1..faee6a08f23c 100644 --- a/xcm/xcm-builder/src/weight.rs +++ b/xcm/xcm-builder/src/weight.rs @@ -14,27 +14,32 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use sp_std::{result::Result, marker::PhantomData, convert::TryInto}; +use frame_support::{ + traits::{tokens::currency::Currency as CurrencyT, Get, OnUnbalanced as OnUnbalancedT}, + weights::{GetDispatchInfo, Weight, WeightToFeePolynomial}, +}; use parity_scale_codec::Decode; -use xcm::v0::{Xcm, Order, MultiAsset, AssetId, MultiLocation, Error, AssetId::Concrete}; -use sp_runtime::traits::{Zero, Saturating, SaturatedConversion}; -use frame_support::traits::{Get, OnUnbalanced as OnUnbalancedT, tokens::currency::Currency as CurrencyT}; -use frame_support::weights::{Weight, GetDispatchInfo, WeightToFeePolynomial}; -use xcm_executor::{Assets, traits::{WeightBounds, WeightTrader}}; +use sp_runtime::traits::{SaturatedConversion, Saturating, Zero}; +use sp_std::{convert::TryInto, marker::PhantomData, result::Result}; +use xcm::v0::{AssetId, AssetId::Concrete, Error, MultiAsset, MultiLocation, Order, Xcm}; +use xcm_executor::{ + traits::{WeightBounds, WeightTrader}, + Assets, +}; pub struct FixedWeightBounds(PhantomData<(T, C)>); impl, C: Decode + GetDispatchInfo> WeightBounds for FixedWeightBounds { fn shallow(message: &mut Xcm) -> Result { Ok(match message { - Xcm::Transact { call, .. } => { - call.ensure_decoded()?.get_dispatch_info().weight.saturating_add(T::get()) - } - Xcm::RelayedFrom { ref mut message, .. } => T::get().saturating_add(Self::shallow(message.as_mut())?), - Xcm::WithdrawAsset { effects, .. } - | Xcm::ReserveAssetDeposited { effects, .. } - | Xcm::TeleportAsset { effects, .. } - => { - let inner: Weight = effects.iter_mut() + Xcm::Transact { call, .. } => + call.ensure_decoded()?.get_dispatch_info().weight.saturating_add(T::get()), + Xcm::RelayedFrom { ref mut message, .. } => + T::get().saturating_add(Self::shallow(message.as_mut())?), + Xcm::WithdrawAsset { effects, .. } | + Xcm::ReserveAssetDeposited { effects, .. } | + Xcm::TeleportAsset { effects, .. } => { + let inner: Weight = effects + .iter_mut() .map(|effect| match effect { Order::BuyExecution { .. } => { // On success, execution of this will result in more weight being consumed but @@ -45,28 +50,29 @@ impl, C: Decode + GetDispatchInfo> WeightBounds for FixedWeigh T::get() }, _ => T::get(), - }).sum(); + }) + .sum(); T::get().saturating_add(inner) - } + }, _ => T::get(), }) } fn deep(message: &mut Xcm) -> Result { Ok(match message { Xcm::RelayedFrom { ref mut message, .. } => Self::deep(message.as_mut())?, - Xcm::WithdrawAsset { effects, .. } - | Xcm::ReserveAssetDeposited { effects, .. } - | Xcm::TeleportAsset { effects, .. } - => { + Xcm::WithdrawAsset { effects, .. } | + Xcm::ReserveAssetDeposited { effects, .. } | + Xcm::TeleportAsset { effects, .. } => { let mut extra = 0; for effect in effects.iter_mut() { match effect { - Order::BuyExecution { xcm, .. } => { + Order::BuyExecution { xcm, .. } => for message in xcm.iter_mut() { - extra.saturating_accrue(Self::shallow(message)?.saturating_add(Self::deep(message)?)); - } - }, - _ => {} + extra.saturating_accrue( + Self::shallow(message)?.saturating_add(Self::deep(message)?), + ); + }, + _ => {}, } } extra @@ -93,13 +99,18 @@ impl TakeRevenue for () { /// The constant `Get` type parameter should be the concrete fungible ID and the amount of it required for /// one second of weight. #[deprecated = "Use `FixedRateOfFungible` instead"] -pub struct FixedRateOfConcreteFungible< - T: Get<(MultiLocation, u128)>, - R: TakeRevenue, ->(Weight, u128, PhantomData<(T, R)>); +pub struct FixedRateOfConcreteFungible, R: TakeRevenue>( + Weight, + u128, + PhantomData<(T, R)>, +); #[allow(deprecated)] -impl, R: TakeRevenue> WeightTrader for FixedRateOfConcreteFungible { - fn new() -> Self { Self(0, 0, PhantomData) } +impl, R: TakeRevenue> WeightTrader + for FixedRateOfConcreteFungible +{ + fn new() -> Self { + Self(0, 0, PhantomData) + } fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { let (id, units_per_second) = T::get(); @@ -137,12 +148,15 @@ impl, R: TakeRevenue> Drop for FixedRateOfConcrete /// /// The constant `Get` type parameter should be the fungible ID and the amount of it required for /// one second of weight. -pub struct FixedRateOfFungible< - T: Get<(AssetId, u128)>, - R: TakeRevenue, ->(Weight, u128, PhantomData<(T, R)>); +pub struct FixedRateOfFungible, R: TakeRevenue>( + Weight, + u128, + PhantomData<(T, R)>, +); impl, R: TakeRevenue> WeightTrader for FixedRateOfFungible { - fn new() -> Self { Self(0, 0, PhantomData) } + fn new() -> Self { + Self(0, 0, PhantomData) + } fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { let (id, units_per_second) = T::get(); @@ -179,20 +193,27 @@ impl, R: TakeRevenue> Drop for FixedRateOfFungible /// Weight trader which uses the `TransactionPayment` pallet to set the right price for weight and then /// places any weight bought into the right account. pub struct UsingComponents< - WeightToFee: WeightToFeePolynomial, + WeightToFee: WeightToFeePolynomial, AssetId: Get, AccountId, Currency: CurrencyT, OnUnbalanced: OnUnbalancedT, ->(Weight, Currency::Balance, PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>); +>( + Weight, + Currency::Balance, + PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>, +); impl< - WeightToFee: WeightToFeePolynomial, - AssetId: Get, - AccountId, - Currency: CurrencyT, - OnUnbalanced: OnUnbalancedT, -> WeightTrader for UsingComponents { - fn new() -> Self { Self(0, Zero::zero(), PhantomData) } + WeightToFee: WeightToFeePolynomial, + AssetId: Get, + AccountId, + Currency: CurrencyT, + OnUnbalanced: OnUnbalancedT, + > WeightTrader for UsingComponents +{ + fn new() -> Self { + Self(0, Zero::zero(), PhantomData) + } fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { let amount = WeightToFee::calc(&weight); @@ -216,15 +237,15 @@ impl< None } } - } impl< - WeightToFee: WeightToFeePolynomial, - AssetId: Get, - AccountId, - Currency: CurrencyT, - OnUnbalanced: OnUnbalancedT, -> Drop for UsingComponents { + WeightToFee: WeightToFeePolynomial, + AssetId: Get, + AccountId, + Currency: CurrencyT, + OnUnbalanced: OnUnbalancedT, + > Drop for UsingComponents +{ fn drop(&mut self) { OnUnbalanced::on_unbalanced(Currency::issue(self.1)); } diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 85ab40a8eb9b..a7e48662e719 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -14,14 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use sp_std::{prelude::*, mem, collections::{btree_map::BTreeMap, btree_set::BTreeSet}}; +use sp_runtime::RuntimeDebug; +use sp_std::{ + collections::{btree_map::BTreeMap, btree_set::BTreeSet}, + mem, + prelude::*, +}; use xcm::v0::{ - MultiAsset, MultiAssets, MultiLocation, AssetInstance, MultiAssetFilter, AssetId, - WildMultiAsset::{All, AllOf}, + AssetId, AssetInstance, Fungibility::{Fungible, NonFungible}, + MultiAsset, MultiAssetFilter, MultiAssets, MultiLocation, WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible}, + WildMultiAsset::{All, AllOf}, }; -use sp_runtime::RuntimeDebug; /// List of non-wildcard fungible and non-fungible assets. #[derive(Default, Clone, RuntimeDebug, Eq, PartialEq)] @@ -80,26 +85,38 @@ pub enum TakeError { impl Assets { /// New value, containing no assets. - pub fn new() -> Self { Self::default() } + pub fn new() -> Self { + Self::default() + } /// A borrowing iterator over the fungible assets. - pub fn fungible_assets_iter<'a>(&'a self) -> impl Iterator + 'a { - self.fungible.iter().map(|(id, &amount)| MultiAsset { fun: Fungible(amount), id: id.clone() }) + pub fn fungible_assets_iter<'a>(&'a self) -> impl Iterator + 'a { + self.fungible + .iter() + .map(|(id, &amount)| MultiAsset { fun: Fungible(amount), id: id.clone() }) } /// A borrowing iterator over the non-fungible assets. - pub fn non_fungible_assets_iter<'a>(&'a self) -> impl Iterator + 'a { - self.non_fungible.iter().map(|(id, instance)| MultiAsset { fun: NonFungible(instance.clone()), id: id.clone() }) + pub fn non_fungible_assets_iter<'a>(&'a self) -> impl Iterator + 'a { + self.non_fungible + .iter() + .map(|(id, instance)| MultiAsset { fun: NonFungible(instance.clone()), id: id.clone() }) } /// A consuming iterator over all assets. - pub fn into_assets_iter(self) -> impl Iterator { - self.fungible.into_iter().map(|(id, amount)| MultiAsset { fun: Fungible(amount), id }) - .chain(self.non_fungible.into_iter().map(|(id, instance)| MultiAsset { fun: NonFungible(instance), id })) + pub fn into_assets_iter(self) -> impl Iterator { + self.fungible + .into_iter() + .map(|(id, amount)| MultiAsset { fun: Fungible(amount), id }) + .chain( + self.non_fungible + .into_iter() + .map(|(id, instance)| MultiAsset { fun: NonFungible(instance), id }), + ) } /// A borrowing iterator over all assets. - pub fn assets_iter<'a>(&'a self) -> impl Iterator + 'a { + pub fn assets_iter<'a>(&'a self) -> impl Iterator + 'a { self.fungible_assets_iter().chain(self.non_fungible_assets_iter()) } @@ -110,7 +127,7 @@ impl Assets { if let (Some(mut f), Some(mut g)) = (f_iter.next(), g_iter.next()) { loop { if f.0 == g.0 { - // keys are equal. in this case, we add `swlf`'s balance for the asset onto `assets`, balance, knowing + // keys are equal. in this case, we add `self`'s balance for the asset onto `assets`, balance, knowing // that the `append` operation which follows will clobber `self`'s value and only use `assets`'s. *f.1 += *g.1; } @@ -142,10 +159,10 @@ impl Assets { .entry(asset.id) .and_modify(|e| *e = e.saturating_add(amount)) .or_insert(amount); - } + }, NonFungible(instance) => { self.non_fungible.insert((asset.id, instance)); - } + }, } } @@ -162,13 +179,21 @@ impl Assets { pub fn prepend_location(&mut self, prepend: &MultiLocation) { let mut fungible = Default::default(); mem::swap(&mut self.fungible, &mut fungible); - self.fungible = fungible.into_iter() - .map(|(mut id, amount)| { let _ = id.reanchor(prepend); (id, amount) }) + self.fungible = fungible + .into_iter() + .map(|(mut id, amount)| { + let _ = id.reanchor(prepend); + (id, amount) + }) .collect(); let mut non_fungible = Default::default(); mem::swap(&mut self.non_fungible, &mut non_fungible); - self.non_fungible = non_fungible.into_iter() - .map(|(mut class, inst)| { let _ = class.reanchor(prepend); (class, inst) }) + self.non_fungible = non_fungible + .into_iter() + .map(|(mut class, inst)| { + let _ = class.reanchor(prepend); + (class, inst) + }) .collect(); } @@ -181,13 +206,13 @@ impl Assets { if self.fungible.get(id).map_or(true, |a| a < amount) { return Err(TakeError::AssetUnderflow((id.clone(), *amount).into())) } - } + }, MultiAsset { fun: NonFungible(ref instance), ref id } => { let id_instance = (id.clone(), instance.clone()); if !self.non_fungible.contains(&id_instance) { return Err(TakeError::AssetUnderflow(id_instance.into())) } - } + }, } } return Ok(()) @@ -202,7 +227,11 @@ impl Assets { /// Returns `Ok` with the definite assets token from `self` and mutates `self` to its value minus /// `mask`. Returns `Err` in the non-saturating case where `self` did not contain (enough of) a definite asset to /// be removed. - fn general_take(&mut self, mask: MultiAssetFilter, saturate: bool) -> Result { + fn general_take( + &mut self, + mask: MultiAssetFilter, + saturate: bool, + ) -> Result { let mut taken = Assets::new(); match mask { MultiAssetFilter::Wild(All) => return Ok(self.swapped(Assets::new())), @@ -210,7 +239,7 @@ impl Assets { if let Some((id, amount)) = self.fungible.remove_entry(&id) { taken.fungible.insert(id, amount); } - } + }, MultiAssetFilter::Wild(AllOf { fun: WildNonFungible, id }) => { let non_fungible = mem::replace(&mut self.non_fungible, Default::default()); non_fungible.into_iter().for_each(|(c, instance)| { @@ -220,7 +249,7 @@ impl Assets { self.non_fungible.insert((c, instance)); } }); - } + }, MultiAssetFilter::Definite(assets) => { if !saturate { self.ensure_contains(&assets)?; @@ -233,7 +262,7 @@ impl Assets { let amount = amount.min(*self_amount); *self_amount -= amount; (*self_amount == 0, amount) - } + }, None => (false, 0), }; if remove { @@ -242,16 +271,16 @@ impl Assets { if amount > 0 { taken.subsume(MultiAsset::from((id, amount)).into()); } - } + }, MultiAsset { fun: NonFungible(instance), id } => { let id_instance = (id, instance); if self.non_fungible.remove(&id_instance) { taken.subsume(id_instance.into()) } - } + }, } } - } + }, } Ok(taken) } @@ -291,12 +320,13 @@ impl Assets { self.fungible.remove(&asset.id); } Ok(self) - } - NonFungible(instance) => if self.non_fungible.remove(&(asset.id, instance)) { - Ok(self) - } else { - Err(self) }, + NonFungible(instance) => + if self.non_fungible.remove(&(asset.id, instance)) { + Ok(self) + } else { + Err(self) + }, } } @@ -325,31 +355,30 @@ impl Assets { if let Some(&amount) = self.fungible.get(&id) { masked.fungible.insert(id.clone(), amount); } - } + }, MultiAssetFilter::Wild(AllOf { fun: WildNonFungible, id }) => { self.non_fungible.iter().for_each(|(ref c, ref instance)| { if c == id { masked.non_fungible.insert((c.clone(), instance.clone())); } }); - } - MultiAssetFilter::Definite(assets) => { + }, + MultiAssetFilter::Definite(assets) => for asset in assets.inner().iter() { match asset { MultiAsset { fun: Fungible(ref amount), ref id } => { if let Some(m) = self.fungible.get(id) { masked.subsume((id.clone(), Fungible(*amount.min(m))).into()); } - } + }, MultiAsset { fun: NonFungible(ref instance), ref id } => { let id_instance = (id.clone(), instance.clone()); if self.non_fungible.contains(&id_instance) { masked.subsume(id_instance.into()); } - } + }, } - } - } + }, } masked } @@ -397,7 +426,9 @@ mod tests { let mut r1 = t1.clone(); r1.subsume_assets(t2.clone()); let mut r2 = t1.clone(); - for a in t2.assets_iter() { r2.subsume(a) } + for a in t2.assets_iter() { + r2.subsume(a) + } assert_eq!(r1, r2); } diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index a12d714cac6c..d6dcbd1f93fd 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -16,17 +16,21 @@ #![cfg_attr(not(feature = "std"), no_std)] -use sp_std::{prelude::*, marker::PhantomData}; use frame_support::{ - ensure, weights::GetDispatchInfo, - dispatch::{Weight, Dispatchable} + dispatch::{Dispatchable, Weight}, + ensure, + weights::GetDispatchInfo, +}; +use sp_std::{marker::PhantomData, prelude::*}; +use xcm::v0::{ + Error as XcmError, ExecuteXcm, MultiAssets, MultiLocation, Order, Outcome, Response, SendXcm, + Xcm, }; -use xcm::v0::{ExecuteXcm, SendXcm, Error as XcmError, Outcome, MultiLocation, MultiAssets, Xcm, Order, Response}; pub mod traits; use traits::{ - TransactAsset, ConvertOrigin, FilterAssetLocation, InvertLocation, WeightBounds, WeightTrader, - ShouldExecute, OnResponse + ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse, ShouldExecute, TransactAsset, + WeightBounds, WeightTrader, }; mod assets; @@ -67,10 +71,17 @@ impl ExecuteXcm for XcmExecutor { None => return Outcome::Error(XcmError::Overflow), }; if maximum_weight > weight_limit { - return Outcome::Error(XcmError::WeightLimitReached(maximum_weight)); + return Outcome::Error(XcmError::WeightLimitReached(maximum_weight)) } let mut trader = Config::Trader::new(); - let result = Self::do_execute_xcm(origin, true, message, &mut weight_credit, Some(shallow_weight), &mut trader); + let result = Self::do_execute_xcm( + origin, + true, + message, + &mut weight_credit, + Some(shallow_weight), + &mut trader, + ); drop(trader); log::trace!(target: "xcm::execute_xcm", "result: {:?}", &result); match result { @@ -115,8 +126,14 @@ impl XcmExecutor { .or_else(|| Config::Weigher::shallow(&mut message).ok()) .ok_or(XcmError::WeightNotComputable)?; - Config::Barrier::should_execute(&origin, top_level, &message, shallow_weight, weight_credit) - .map_err(|()| XcmError::Barrier)?; + Config::Barrier::should_execute( + &origin, + top_level, + &message, + shallow_weight, + weight_credit, + ) + .map_err(|()| XcmError::Barrier)?; // The surplus weight, defined as the amount by which `shallow_weight` plus all nested // `shallow_weight` values (ensuring no double-counting and also known as `deep_weight`) is an @@ -132,23 +149,26 @@ impl XcmExecutor { holding.subsume_assets(withdrawn); } Some((holding, effects)) - } + }, (origin, Xcm::ReserveAssetDeposited { assets, effects }) => { // check whether we trust origin to be our reserve location for this asset. for asset in assets.inner() { // We only trust the origin to send us assets that they identify as their // sovereign assets. - ensure!(Config::IsReserve::filter_asset_location(asset, &origin), XcmError::UntrustedReserveLocation); + ensure!( + Config::IsReserve::filter_asset_location(asset, &origin), + XcmError::UntrustedReserveLocation + ); } Some((assets.into(), effects)) - } + }, (origin, Xcm::TransferAsset { assets, dest }) => { // Take `assets` from the origin account (on-chain) and place into dest account. for asset in assets.inner() { Config::AssetTransactor::beam_asset(&asset, &origin, &dest)?; } None - } + }, (origin, Xcm::TransferReserveAsset { mut assets, dest, effects }) => { // Take `assets` from the origin account (on-chain) and place into dest account. let inv_dest = Config::LocationInverter::invert_location(&dest); @@ -158,13 +178,16 @@ impl XcmExecutor { assets.reanchor(&inv_dest)?; Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposited { assets, effects })?; None - } + }, (origin, Xcm::TeleportAsset { assets, effects }) => { // check whether we trust origin to teleport this asset to us via config trait. for asset in assets.inner() { // We only trust the origin to send us assets that they identify as their // sovereign assets. - ensure!(Config::IsTeleporter::filter_asset_location(asset, &origin), XcmError::UntrustedTeleportLocation); + ensure!( + Config::IsTeleporter::filter_asset_location(asset, &origin), + XcmError::UntrustedTeleportLocation + ); // We should check that the asset can actually be teleported in (for this to be in error, there // would need to be an accounting violation by one of the trusted chains, so it's unlikely, but we // don't want to punish a possibly innocent chain/user). @@ -174,7 +197,7 @@ impl XcmExecutor { Config::AssetTransactor::check_in(&origin, asset); } Some((Assets::from(assets), effects)) - } + }, (origin, Xcm::Transact { origin_type, require_weight_at_most, mut call }) => { // We assume that the Relay-chain is allowed to use transact on this parachain. @@ -190,8 +213,9 @@ impl XcmExecutor { // Not much to do with the result as it is. It's up to the parachain to ensure that the // message makes sense. error_and_info.post_info.actual_weight - } - }.unwrap_or(weight); + }, + } + .unwrap_or(weight); let surplus = weight.saturating_sub(actual_weight); // Credit any surplus weight that we bought. This should be safe since it's work we // didn't realise that we didn't have to do. @@ -204,20 +228,21 @@ impl XcmExecutor { // Return the overestimated amount so we can adjust our expectations on how much this entire // execution has taken. None - } + }, (origin, Xcm::QueryResponse { query_id, response }) => { Config::ResponseHandler::on_response(origin, query_id, response); None - } + }, (origin, Xcm::RelayedFrom { who, message }) => { ensure!(who.is_interior(), XcmError::EscalationOfPrivilege); let mut origin = origin; origin.append_with(who).map_err(|_| XcmError::MultiLocationFull)?; - let surplus = Self::do_execute_xcm(origin, top_level, *message, weight_credit, None, trader)?; + let surplus = + Self::do_execute_xcm(origin, top_level, *message, weight_credit, None, trader)?; total_surplus = total_surplus.saturating_add(surplus); None - } - _ => Err(XcmError::UnhandledXcmMessage)?, // Unhandled XCM message. + }, + _ => Err(XcmError::UnhandledXcmMessage)?, // Unhandled XCM message. }; if let Some((mut holding, effects)) = maybe_holding_effects { @@ -258,11 +283,11 @@ impl XcmExecutor { let assets = Self::reanchored(deposited, &dest); Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposited { assets, effects })?; }, - Order::InitiateReserveWithdraw { assets, reserve, effects} => { + Order::InitiateReserveWithdraw { assets, reserve, effects } => { let assets = Self::reanchored(holding.saturating_take(assets), &reserve); Config::XcmSender::send_xcm(reserve, Xcm::WithdrawAsset { assets, effects })?; - } - Order::InitiateTeleport { assets, dest, effects} => { + }, + Order::InitiateTeleport { assets, dest, effects } => { // We must do this first in order to resolve wildcards. let assets = holding.saturating_take(assets); for asset in assets.assets_iter() { @@ -270,30 +295,42 @@ impl XcmExecutor { } let assets = Self::reanchored(assets, &dest); Config::XcmSender::send_xcm(dest, Xcm::TeleportAsset { assets, effects })?; - } + }, Order::QueryHolding { query_id, dest, assets } => { let assets = Self::reanchored(holding.min(&assets), &dest); - Config::XcmSender::send_xcm(dest, Xcm::QueryResponse { query_id, response: Response::Assets(assets) })?; - } + Config::XcmSender::send_xcm( + dest, + Xcm::QueryResponse { query_id, response: Response::Assets(assets) }, + )?; + }, Order::BuyExecution { fees, weight, debt, halt_on_error, xcm } => { // pay for `weight` using up to `fees` of the holding register. - let purchasing_weight = Weight::from(weight.checked_add(debt).ok_or(XcmError::Overflow)?); - let max_fee = holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?; + let purchasing_weight = + Weight::from(weight.checked_add(debt).ok_or(XcmError::Overflow)?); + let max_fee = + holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?; let unspent = trader.buy_weight(purchasing_weight, max_fee)?; holding.subsume_assets(unspent); let mut remaining_weight = weight; for message in xcm.into_iter() { - match Self::do_execute_xcm(origin.clone(), false, message, &mut remaining_weight, None, trader) { + match Self::do_execute_xcm( + origin.clone(), + false, + message, + &mut remaining_weight, + None, + trader, + ) { Err(e) if halt_on_error => return Err(e), - Err(_) => {} - Ok(surplus) => { total_surplus += surplus } + Err(_) => {}, + Ok(surplus) => total_surplus += surplus, } } if let Some(w) = trader.refund_weight(remaining_weight) { holding.subsume(w); } - } + }, _ => return Err(XcmError::UnhandledEffect)?, } Ok(total_surplus) diff --git a/xcm/xcm-executor/src/traits/transact_asset.rs b/xcm/xcm-executor/src/traits/transact_asset.rs index 09cd94b19918..d6114efbe09c 100644 --- a/xcm/xcm-executor/src/traits/transact_asset.rs +++ b/xcm/xcm-executor/src/traits/transact_asset.rs @@ -14,9 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use sp_std::result::Result; -use xcm::v0::{Error as XcmError, Result as XcmResult, MultiAsset, MultiLocation}; use crate::Assets; +use sp_std::result::Result; +use xcm::v0::{Error as XcmError, MultiAsset, MultiLocation, Result as XcmResult}; /// Facility for asset transacting. /// @@ -78,22 +78,30 @@ pub trait TransactAsset { /// Move an `asset` `from` one location in `to` another location. /// /// Returns `XcmError::FailedToTransactAsset` if transfer failed. - fn transfer_asset(_asset: &MultiAsset, _from: &MultiLocation, _to: &MultiLocation) -> Result { + fn transfer_asset( + _asset: &MultiAsset, + _from: &MultiLocation, + _to: &MultiLocation, + ) -> Result { Err(XcmError::Unimplemented) } /// Move an `asset` `from` one location in `to` another location. /// /// Attempts to use `transfer_asset` and if not available then falls back to using a two-part withdraw/deposit. - fn beam_asset(asset: &MultiAsset, from: &MultiLocation, to: &MultiLocation) -> Result { + fn beam_asset( + asset: &MultiAsset, + from: &MultiLocation, + to: &MultiLocation, + ) -> Result { match Self::transfer_asset(asset, from, to) { Err(XcmError::Unimplemented) => { let assets = Self::withdraw_asset(asset, from)?; // Not a very forgiving attitude; once we implement roll-backs then it'll be nicer. Self::deposit_asset(asset, to)?; Ok(assets) - } - result => result + }, + result => result, } } } @@ -160,7 +168,11 @@ impl TransactAsset for Tuple { Err(XcmError::AssetNotFound) } - fn transfer_asset(what: &MultiAsset, from: &MultiLocation, to: &MultiLocation) -> Result { + fn transfer_asset( + what: &MultiAsset, + from: &MultiLocation, + to: &MultiLocation, + ) -> Result { for_tuples!( #( match Tuple::transfer_asset(what, from, to) { Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (), @@ -200,7 +212,11 @@ mod tests { Err(XcmError::AssetNotFound) } - fn transfer_asset(_what: &MultiAsset, _from: &MultiLocation, _to: &MultiLocation) -> Result { + fn transfer_asset( + _what: &MultiAsset, + _from: &MultiLocation, + _to: &MultiLocation, + ) -> Result { Err(XcmError::AssetNotFound) } } @@ -219,7 +235,11 @@ mod tests { Err(XcmError::Overflow) } - fn transfer_asset(_what: &MultiAsset, _from: &MultiLocation, _to: &MultiLocation) -> Result { + fn transfer_asset( + _what: &MultiAsset, + _from: &MultiLocation, + _to: &MultiLocation, + ) -> Result { Err(XcmError::Overflow) } } @@ -238,16 +258,24 @@ mod tests { Ok(Assets::default()) } - fn transfer_asset(_what: &MultiAsset, _from: &MultiLocation, _to: &MultiLocation) -> Result { + fn transfer_asset( + _what: &MultiAsset, + _from: &MultiLocation, + _to: &MultiLocation, + ) -> Result { Ok(Assets::default()) } } #[test] fn defaults_to_asset_not_found() { - type MultiTransactor = (UnimplementedTransactor, NotFoundTransactor, UnimplementedTransactor); + type MultiTransactor = + (UnimplementedTransactor, NotFoundTransactor, UnimplementedTransactor); - assert_eq!(MultiTransactor::deposit_asset(&(Null, 1).into(), &Null), Err(XcmError::AssetNotFound)); + assert_eq!( + MultiTransactor::deposit_asset(&(Null, 1).into(), &Null), + Err(XcmError::AssetNotFound) + ); } #[test] @@ -261,7 +289,10 @@ mod tests { fn unexpected_error_stops_iteration() { type MultiTransactor = (OverflowTransactor, SuccessfulTransactor); - assert_eq!(MultiTransactor::deposit_asset(&(Null, 1).into(), &Null), Err(XcmError::Overflow)); + assert_eq!( + MultiTransactor::deposit_asset(&(Null, 1).into(), &Null), + Err(XcmError::Overflow) + ); } #[test] diff --git a/xcm/xcm-executor/src/traits/weight.rs b/xcm/xcm-executor/src/traits/weight.rs index 1844f3b6c7f2..4e9c805edfbd 100644 --- a/xcm/xcm-executor/src/traits/weight.rs +++ b/xcm/xcm-executor/src/traits/weight.rs @@ -14,10 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use sp_std::result::Result; -use xcm::v0::{Xcm, MultiAsset, MultiLocation, Error}; -use frame_support::weights::Weight; use crate::Assets; +use frame_support::weights::Weight; +use sp_std::result::Result; +use xcm::v0::{Error, MultiAsset, MultiLocation, Xcm}; /// Determine the weight of an XCM message. pub trait WeightBounds { @@ -71,11 +71,15 @@ pub trait WeightTrader: Sized { /// purchased using `buy_weight`. /// /// Default implementation refunds nothing. - fn refund_weight(&mut self, _weight: Weight) -> Option { None } + fn refund_weight(&mut self, _weight: Weight) -> Option { + None + } } impl WeightTrader for () { - fn new() -> Self { () } + fn new() -> Self { + () + } fn buy_weight(&mut self, _: Weight, _: Assets) -> Result { Err(Error::Unimplemented) } From 9e2e009e4ba12f663b45f10563a2b80e7043d287 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 3 Aug 2021 20:21:54 +0200 Subject: [PATCH 53/84] Move to XCM version 1 --- runtime/kusama/src/lib.rs | 2 +- runtime/parachains/src/dmp.rs | 2 +- runtime/parachains/src/ump.rs | 4 +- runtime/rococo/src/lib.rs | 2 +- runtime/westend/src/lib.rs | 2 +- xcm/pallet-xcm/src/lib.rs | 4 +- xcm/pallet-xcm/src/mock.rs | 2 +- xcm/src/lib.rs | 66 +++++++++++++++---- xcm/src/{v0 => v1}/junction.rs | 0 xcm/src/{v0 => v1}/mod.rs | 21 +----- xcm/src/{v0 => v1}/multiasset.rs | 0 .../multi_location.rs => v1/multilocation.rs} | 23 ++----- xcm/src/{v0 => v1}/order.rs | 0 xcm/src/{v0 => v1}/traits.rs | 2 +- xcm/xcm-builder/src/barriers.rs | 2 +- xcm/xcm-builder/src/currency_adapter.rs | 4 +- xcm/xcm-builder/src/filter_asset_location.rs | 2 +- xcm/xcm-builder/src/fungibles_adapter.rs | 2 +- xcm/xcm-builder/src/location_conversion.rs | 6 +- xcm/xcm-builder/src/matches_fungible.rs | 6 +- xcm/xcm-builder/src/mock.rs | 6 +- xcm/xcm-builder/src/origin_conversion.rs | 2 +- xcm/xcm-builder/src/tests.rs | 2 +- xcm/xcm-builder/src/weight.rs | 2 +- xcm/xcm-executor/src/assets.rs | 6 +- xcm/xcm-executor/src/config.rs | 2 +- xcm/xcm-executor/src/lib.rs | 2 +- xcm/xcm-executor/src/traits/conversion.rs | 4 +- .../src/traits/filter_asset_location.rs | 2 +- .../src/traits/matches_fungible.rs | 2 +- .../src/traits/matches_fungibles.rs | 2 +- xcm/xcm-executor/src/traits/on_response.rs | 2 +- xcm/xcm-executor/src/traits/should_execute.rs | 2 +- xcm/xcm-executor/src/traits/transact_asset.rs | 2 +- xcm/xcm-executor/src/traits/weight.rs | 2 +- xcm/xcm-simulator/example/src/lib.rs | 2 +- xcm/xcm-simulator/example/src/relay_chain.rs | 2 +- 37 files changed, 105 insertions(+), 91 deletions(-) rename xcm/src/{v0 => v1}/junction.rs (100%) rename xcm/src/{v0 => v1}/mod.rs (96%) rename xcm/src/{v0 => v1}/multiasset.rs (100%) rename xcm/src/{v0/multi_location.rs => v1/multilocation.rs} (98%) rename xcm/src/{v0 => v1}/order.rs (100%) rename xcm/src/{v0 => v1}/traits.rs (99%) diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index 705bd319efae..c2720c8deed1 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -75,7 +75,7 @@ use sp_staking::SessionIndex; use sp_version::NativeVersion; use sp_version::RuntimeVersion; use static_assertions::const_assert; -use xcm::v0::prelude::*; +use xcm::v1::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, BackingToPlurality, ChildParachainAsNative, ChildParachainConvertsVia, diff --git a/runtime/parachains/src/dmp.rs b/runtime/parachains/src/dmp.rs index 7982424800e5..1ff8fed4528d 100644 --- a/runtime/parachains/src/dmp.rs +++ b/runtime/parachains/src/dmp.rs @@ -22,7 +22,7 @@ use frame_support::pallet_prelude::*; use primitives::v1::{DownwardMessage, Hash, Id as ParaId, InboundDownwardMessage}; use sp_runtime::traits::{BlakeTwo256, Hash as HashT, SaturatedConversion}; use sp_std::{fmt, prelude::*}; -use xcm::v0::Error as XcmError; +use xcm::v1::Error as XcmError; pub use pallet::*; diff --git a/runtime/parachains/src/ump.rs b/runtime/parachains/src/ump.rs index c64123537b02..32e7edadaf79 100644 --- a/runtime/parachains/src/ump.rs +++ b/runtime/parachains/src/ump.rs @@ -27,7 +27,7 @@ use sp_std::{ marker::PhantomData, prelude::*, }; -use xcm::v0::Outcome; +use xcm::v1::Outcome; pub use pallet::*; @@ -78,7 +78,7 @@ pub type MessageId = [u8; 32]; /// and will be forwarded to the XCM Executor. pub struct XcmSink(PhantomData<(XcmExecutor, Config)>); -impl, C: Config> UmpSink for XcmSink { +impl, C: Config> UmpSink for XcmSink { fn process_upward_message( origin: ParaId, data: &[u8], diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 4086d38ca814..88e6072003ff 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -80,7 +80,7 @@ use polkadot_parachain::primitives::Id as ParaId; use constants::{currency::*, fee::*, time::*}; use frame_support::traits::InstanceFilter; -use xcm::v0::prelude::*; +use xcm::v1::prelude::*; use xcm_builder::{ AccountId32Aliases, BackingToPlurality, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedWeightBounds, diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 12efa280771d..a578ff548afc 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -44,7 +44,7 @@ use runtime_parachains::{ session_info as parachains_session_info, shared as parachains_shared, ump as parachains_ump, }; -use xcm::v0::prelude::*; +use xcm::v1::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index 95d5c867e781..ece2590f4d53 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -27,7 +27,7 @@ use codec::{Decode, Encode}; use frame_support::traits::{Contains, EnsureOrigin, Filter, Get, OriginTrait}; use sp_runtime::{traits::BadOrigin, RuntimeDebug}; use sp_std::{boxed::Box, convert::TryInto, marker::PhantomData, prelude::*, vec}; -use xcm::v0::prelude::*; +use xcm::v1::prelude::*; use xcm_executor::traits::ConvertOrigin; use frame_support::PalletId; @@ -82,7 +82,7 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - Attempted(xcm::v0::Outcome), + Attempted(xcm::v1::Outcome), Sent(MultiLocation, MultiLocation, Xcm<()>), } diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index bd8375dec3b8..eb9136b4ed03 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -181,7 +181,7 @@ impl pallet_xcm::Config for Test { type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; type XcmRouter = (TestSendXcmErrX8, TestSendXcm); type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; - type XcmExecuteFilter = All<(MultiLocation, xcm::v0::Xcm)>; + type XcmExecuteFilter = All<(MultiLocation, xcm::v1::Xcm)>; type XcmExecutor = XcmExecutor; type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; diff --git a/xcm/src/lib.rs b/xcm/src/lib.rs index 4a275d61c4e3..705b4d34933b 100644 --- a/xcm/src/lib.rs +++ b/xcm/src/lib.rs @@ -23,25 +23,50 @@ #![no_std] extern crate alloc; +use core::{result::Result, convert::TryFrom}; use derivative::Derivative; -use parity_scale_codec::{Decode, Encode}; +use parity_scale_codec::{Decode, Encode, Input, Error as CodecError}; -pub mod v0; +pub mod v1; mod double_encoded; pub use double_encoded::DoubleEncoded; +pub enum Unsupported {} +impl Decode for Unsupported { + fn decode(_: &mut I) -> Result { + Err("Not decodable".into()) + } +} + /// A single XCM message, together with its version code. #[derive(Derivative, Encode, Decode)] #[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] #[codec(encode_bound())] #[codec(decode_bound())] pub enum VersionedXcm { - V0(v0::Xcm), + V0(Unsupported), + V1(v1::Xcm), +} + +impl From> for VersionedXcm { + fn from(x: v1::Xcm) -> Self { + VersionedXcm::V0(x) + } +} + +impl TryFrom> for v1::Xcm { + type Error = (); + fn try_from(x: VersionedXcm) -> Result { + match x { + VersionedXcm::V1(x) => Ok(x), + _ => Err(()), + } + } } pub mod opaque { - pub mod v0 { + pub mod v1 { // Everything from v0 pub use crate::v0::*; // Then override with the opaque types in v0 @@ -55,26 +80,45 @@ pub mod opaque { /// A versioned multi-location, a relative location of a cross-consensus system identifier. #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)] pub enum VersionedMultiLocation { - V0(v0::MultiLocation), + V0(v1::MultiLocation), + V1(v1::MultiLocation), +} + +impl From for VersionedMultiLocation { + fn from(x: v1::MultiLocation) -> Self { + VersionedMultiLocation::V1(x) + } +} + +impl TryFrom for v1::MultiLocation { + type Error = (); + fn try_from(x: VersionedMultiLocation) -> Result { + match x { + VersionedMultiLocation::V0(x) => Ok(x), + VersionedMultiLocation::V1(x) => Ok(x), + } + } } /// A versioned multi-asset, an identifier for an asset within a consensus system. #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)] pub enum VersionedMultiAsset { - V0(v0::MultiAsset), + V0(Unsupported), + V1(v1::MultiAsset), } -impl From for VersionedMultiAsset { - fn from(x: v0::MultiAsset) -> Self { - VersionedMultiAsset::V0(x) +impl From for VersionedMultiAsset { + fn from(x: v1::MultiAsset) -> Self { + VersionedMultiAsset::V1(x) } } -impl core::convert::TryFrom for v0::MultiAsset { +impl core::convert::TryFrom for v1::MultiAsset { type Error = (); fn try_from(x: VersionedMultiAsset) -> core::result::Result { match x { - VersionedMultiAsset::V0(x) => Ok(x), + VersionedMultiAsset::V1(x) => Ok(x), + _ => Err(()), } } } diff --git a/xcm/src/v0/junction.rs b/xcm/src/v1/junction.rs similarity index 100% rename from xcm/src/v0/junction.rs rename to xcm/src/v1/junction.rs diff --git a/xcm/src/v0/mod.rs b/xcm/src/v1/mod.rs similarity index 96% rename from xcm/src/v0/mod.rs rename to xcm/src/v1/mod.rs index 2bd568e60194..eb97acdeebde 100644 --- a/xcm/src/v0/mod.rs +++ b/xcm/src/v1/mod.rs @@ -23,13 +23,13 @@ use derivative::Derivative; use parity_scale_codec::{self, Decode, Encode}; mod junction; -mod multi_location; +mod multilocation; pub mod multiasset; mod order; mod traits; // the new multiasset. pub use junction::{BodyId, BodyPart, Junction, NetworkId}; -pub use multi_location::MultiLocation; +pub use multilocation::MultiLocation; pub use multiasset::{ AssetId, AssetInstance, Fungibility, MultiAsset, MultiAssetFilter, MultiAssets, WildFungibility, WildMultiAsset, @@ -45,7 +45,7 @@ pub mod prelude { Junction::*, NetworkId::{self, *}, }, - multi_location::MultiLocation::{self, *}, + multilocation::MultiLocation::{self, *}, multiasset::{ AssetId::{self, *}, AssetInstance::{self, *}, @@ -290,21 +290,6 @@ pub enum Xcm { RelayedFrom { who: MultiLocation, message: alloc::boxed::Box> }, } -impl From> for VersionedXcm { - fn from(x: Xcm) -> Self { - VersionedXcm::V0(x) - } -} - -impl TryFrom> for Xcm { - type Error = (); - fn try_from(x: VersionedXcm) -> result::Result { - match x { - VersionedXcm::V0(x) => Ok(x), - } - } -} - impl Xcm { pub fn into(self) -> Xcm { Xcm::from(self) diff --git a/xcm/src/v0/multiasset.rs b/xcm/src/v1/multiasset.rs similarity index 100% rename from xcm/src/v0/multiasset.rs rename to xcm/src/v1/multiasset.rs diff --git a/xcm/src/v0/multi_location.rs b/xcm/src/v1/multilocation.rs similarity index 98% rename from xcm/src/v0/multi_location.rs rename to xcm/src/v1/multilocation.rs index e45df25a0ff6..af1b289702b2 100644 --- a/xcm/src/v0/multi_location.rs +++ b/xcm/src/v1/multilocation.rs @@ -460,7 +460,7 @@ impl MultiLocation { /// /// # Example /// ```rust - /// # use xcm::v0::{MultiLocation::*, Junction::*}; + /// # use xcm::v1::{MultiLocation::*, Junction::*}; /// # fn main() { /// let mut m = X3(Parent, PalletInstance(3), OnlyChild); /// assert_eq!(m.match_and_split(&X2(Parent, PalletInstance(3))), Some(&OnlyChild)); @@ -600,7 +600,7 @@ impl MultiLocation { /// /// # Example /// ```rust - /// # use xcm::v0::{MultiLocation::*, Junction::*}; + /// # use xcm::v1::{MultiLocation::*, Junction::*}; /// # fn main() { /// let mut m = X3(Parent, Parachain(21), OnlyChild); /// assert_eq!(m.append_with(X2(Parent, PalletInstance(3))), Ok(())); @@ -627,7 +627,7 @@ impl MultiLocation { /// /// # Example /// ```rust - /// # use xcm::v0::{MultiLocation::*, Junction::*, NetworkId::Any}; + /// # use xcm::v1::{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(())); @@ -684,7 +684,7 @@ impl MultiLocation { /// /// # Example /// ```rust - /// # use xcm::v0::{MultiLocation::*, Junction::*, NetworkId::Any}; + /// # use xcm::v1::{MultiLocation::*, Junction::*, NetworkId::Any}; /// # fn main() { /// let parent = X1(Parent); /// assert_eq!(parent.is_interior(), false); @@ -697,21 +697,6 @@ impl MultiLocation { } } -impl From for VersionedMultiLocation { - fn from(x: MultiLocation) -> Self { - VersionedMultiLocation::V0(x) - } -} - -impl TryFrom for MultiLocation { - type Error = (); - fn try_from(x: VersionedMultiLocation) -> result::Result { - match x { - VersionedMultiLocation::V0(x) => Ok(x), - } - } -} - #[cfg(test)] mod tests { use super::MultiLocation::*; diff --git a/xcm/src/v0/order.rs b/xcm/src/v1/order.rs similarity index 100% rename from xcm/src/v0/order.rs rename to xcm/src/v1/order.rs diff --git a/xcm/src/v0/traits.rs b/xcm/src/v1/traits.rs similarity index 99% rename from xcm/src/v0/traits.rs rename to xcm/src/v1/traits.rs index d6ce316726c8..fc1c3ce88b57 100644 --- a/xcm/src/v0/traits.rs +++ b/xcm/src/v1/traits.rs @@ -184,7 +184,7 @@ impl ExecuteXcm for () { /// /// # Example /// ```rust -/// # use xcm::v0::{MultiLocation, Xcm, Junction, Error, OriginKind, SendXcm, Result}; +/// # use xcm::v1::{MultiLocation, Xcm, Junction, Error, OriginKind, SendXcm, Result}; /// # use parity_scale_codec::Encode; /// /// /// A sender that only passes the message through and does nothing. diff --git a/xcm/xcm-builder/src/barriers.rs b/xcm/xcm-builder/src/barriers.rs index 2d870d7525c2..f253be18026f 100644 --- a/xcm/xcm-builder/src/barriers.rs +++ b/xcm/xcm-builder/src/barriers.rs @@ -19,7 +19,7 @@ use frame_support::{ensure, traits::Contains, weights::Weight}; use polkadot_parachain::primitives::IsSystem; use sp_std::{marker::PhantomData, result::Result}; -use xcm::v0::{Junction, MultiLocation, Order, Xcm}; +use xcm::v1::{Junction, MultiLocation, Order, Xcm}; use xcm_executor::traits::{OnResponse, ShouldExecute}; /// Execution barrier that just takes `shallow_weight` from `weight_credit`. diff --git a/xcm/xcm-builder/src/currency_adapter.rs b/xcm/xcm-builder/src/currency_adapter.rs index fcbaa93c5998..ebbdf794209f 100644 --- a/xcm/xcm-builder/src/currency_adapter.rs +++ b/xcm/xcm-builder/src/currency_adapter.rs @@ -19,7 +19,7 @@ use frame_support::traits::{ExistenceRequirement::AllowDeath, Get, WithdrawReasons}; use sp_runtime::traits::{CheckedSub, SaturatedConversion}; use sp_std::{convert::TryInto, marker::PhantomData, result}; -use xcm::v0::{Error as XcmError, MultiAsset, MultiLocation, Result}; +use xcm::v1::{Error as XcmError, MultiAsset, MultiLocation, Result}; use xcm_executor::{ traits::{Convert, MatchesFungible, TransactAsset}, Assets, @@ -53,7 +53,7 @@ impl From for XcmError { /// # Example /// ``` /// use frame_support::parameter_types; -/// use xcm::v0::{MultiLocation, Junction}; +/// use xcm::v1::{MultiLocation, Junction}; /// use xcm_builder::{ParentIsDefault, CurrencyAdapter, IsConcrete}; /// /// /// Our chain's account id. diff --git a/xcm/xcm-builder/src/filter_asset_location.rs b/xcm/xcm-builder/src/filter_asset_location.rs index cb2fa80be734..99a0e5e8f7f6 100644 --- a/xcm/xcm-builder/src/filter_asset_location.rs +++ b/xcm/xcm-builder/src/filter_asset_location.rs @@ -18,7 +18,7 @@ use frame_support::traits::Get; use sp_std::marker::PhantomData; -use xcm::v0::{AssetId::Concrete, MultiAsset, MultiAssetFilter, MultiLocation}; +use xcm::v1::{AssetId::Concrete, MultiAsset, MultiAssetFilter, MultiLocation}; use xcm_executor::traits::FilterAssetLocation; /// Accepts an asset iff it is a native asset. diff --git a/xcm/xcm-builder/src/fungibles_adapter.rs b/xcm/xcm-builder/src/fungibles_adapter.rs index d81ddd5a575d..048f3d8d3a52 100644 --- a/xcm/xcm-builder/src/fungibles_adapter.rs +++ b/xcm/xcm-builder/src/fungibles_adapter.rs @@ -18,7 +18,7 @@ use frame_support::traits::{tokens::fungibles, Contains, Get}; use sp_std::{borrow::Borrow, marker::PhantomData, prelude::*, result}; -use xcm::v0::{ +use xcm::v1::{ AssetId::{Abstract, Concrete}, Error as XcmError, Fungibility::Fungible, diff --git a/xcm/xcm-builder/src/location_conversion.rs b/xcm/xcm-builder/src/location_conversion.rs index 23612629946e..cbaf0b93063e 100644 --- a/xcm/xcm-builder/src/location_conversion.rs +++ b/xcm/xcm-builder/src/location_conversion.rs @@ -19,7 +19,7 @@ use parity_scale_codec::Encode; use sp_io::hashing::blake2_256; use sp_runtime::traits::AccountIdConversion; use sp_std::{borrow::Borrow, marker::PhantomData}; -use xcm::v0::{Junction, MultiLocation, NetworkId}; +use xcm::v1::{Junction, MultiLocation, NetworkId}; use xcm_executor::traits::{Convert, InvertLocation}; pub struct Account32Hash(PhantomData<(Network, AccountId)>); @@ -155,7 +155,7 @@ impl, AccountId: From<[u8; 20]> + Into<[u8; 20]> + Clone /// ``` /// ```rust /// # use frame_support::parameter_types; -/// # use xcm::v0::{MultiLocation::{self, *}, Junction::*, NetworkId::Any}; +/// # use xcm::v1::{MultiLocation::{self, *}, Junction::*, NetworkId::Any}; /// # use xcm_builder::LocationInverter; /// # use xcm_executor::traits::InvertLocation; /// # fn main() { @@ -200,7 +200,7 @@ mod tests { use super::*; use frame_support::parameter_types; - use xcm::v0::{Junction::*, MultiLocation::*, NetworkId::Any}; + use xcm::v1::{Junction::*, MultiLocation::*, NetworkId::Any}; fn account20() -> Junction { AccountKey20 { network: Any, key: Default::default() } diff --git a/xcm/xcm-builder/src/matches_fungible.rs b/xcm/xcm-builder/src/matches_fungible.rs index 0b635f3c1cb8..8e61bd2f8439 100644 --- a/xcm/xcm-builder/src/matches_fungible.rs +++ b/xcm/xcm-builder/src/matches_fungible.rs @@ -19,7 +19,7 @@ use frame_support::traits::Get; use sp_runtime::traits::CheckedConversion; use sp_std::{convert::TryFrom, marker::PhantomData}; -use xcm::v0::{ +use xcm::v1::{ AssetId::{Abstract, Concrete}, Fungibility::Fungible, MultiAsset, MultiLocation, @@ -32,7 +32,7 @@ use xcm_executor::traits::MatchesFungible; /// # Example /// /// ``` -/// use xcm::v0::prelude::*; +/// use xcm::v1::prelude::*; /// use xcm_builder::IsConcrete; /// use xcm_executor::traits::MatchesFungible; /// @@ -62,7 +62,7 @@ impl, B: TryFrom> MatchesFungible for IsConcrete< /// # Example /// /// ``` -/// use xcm::v0::prelude::*; +/// use xcm::v1::prelude::*; /// use xcm_builder::IsAbstract; /// use xcm_executor::traits::MatchesFungible; /// diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index 6adc5b478343..c8d6036577e7 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -34,7 +34,7 @@ pub use sp_std::{ fmt::Debug, marker::PhantomData, }; -pub use xcm::v0::prelude::*; +pub use xcm::v1::prelude::*; pub use xcm_executor::{ traits::{ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset}, Assets, Config, @@ -203,7 +203,7 @@ impl FilterAssetLocation for TestIsTeleporter { } } -use xcm::v0::Response; +use xcm::v1::Response; pub enum ResponseSlot { Expecting(MultiLocation), Received(Response), @@ -219,7 +219,7 @@ impl OnResponse for TestResponseHandler { _ => false, }) } - fn on_response(_origin: MultiLocation, query_id: u64, response: xcm::v0::Response) -> Weight { + fn on_response(_origin: MultiLocation, query_id: u64, response: xcm::v1::Response) -> Weight { QUERIES.with(|q| { q.borrow_mut().entry(query_id).and_modify(|v| { if matches!(*v, ResponseSlot::Expecting(..)) { diff --git a/xcm/xcm-builder/src/origin_conversion.rs b/xcm/xcm-builder/src/origin_conversion.rs index 2a1956ca79de..2cc343694fa9 100644 --- a/xcm/xcm-builder/src/origin_conversion.rs +++ b/xcm/xcm-builder/src/origin_conversion.rs @@ -20,7 +20,7 @@ use frame_support::traits::{EnsureOrigin, Get, GetBacking, OriginTrait}; use frame_system::RawOrigin as SystemRawOrigin; use polkadot_parachain::primitives::IsSystem; use sp_std::{convert::TryInto, marker::PhantomData}; -use xcm::v0::{BodyId, BodyPart, Junction, MultiLocation, NetworkId, OriginKind}; +use xcm::v1::{BodyId, BodyPart, Junction, MultiLocation, NetworkId, OriginKind}; use xcm_executor::traits::{Convert, ConvertOrigin}; /// Sovereign accounts use the system's `Signed` origin with an account ID derived from the `LocationConverter`. diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index 5c7465440b29..93c87ce5dad5 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -15,7 +15,7 @@ // along with Polkadot. If not, see . use super::{mock::*, *}; -use xcm::v0::prelude::*; +use xcm::v1::prelude::*; use xcm_executor::{traits::*, Config, XcmExecutor}; #[test] diff --git a/xcm/xcm-builder/src/weight.rs b/xcm/xcm-builder/src/weight.rs index faee6a08f23c..87b7c2f6b488 100644 --- a/xcm/xcm-builder/src/weight.rs +++ b/xcm/xcm-builder/src/weight.rs @@ -21,7 +21,7 @@ use frame_support::{ use parity_scale_codec::Decode; use sp_runtime::traits::{SaturatedConversion, Saturating, Zero}; use sp_std::{convert::TryInto, marker::PhantomData, result::Result}; -use xcm::v0::{AssetId, AssetId::Concrete, Error, MultiAsset, MultiLocation, Order, Xcm}; +use xcm::v1::{AssetId, AssetId::Concrete, Error, MultiAsset, MultiLocation, Order, Xcm}; use xcm_executor::{ traits::{WeightBounds, WeightTrader}, Assets, diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index a7e48662e719..5270a30d44d2 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -20,7 +20,7 @@ use sp_std::{ mem, prelude::*, }; -use xcm::v0::{ +use xcm::v1::{ AssetId, AssetInstance, Fungibility::{Fungible, NonFungible}, MultiAsset, MultiAssetFilter, MultiAssets, MultiLocation, @@ -338,7 +338,7 @@ impl Assets { /// /// ``` /// use xcm_executor::Assets; - /// use xcm::v0::prelude::*; + /// use xcm::v1::prelude::*; /// let assets_i_have: Assets = vec![ (Null, 100).into(), (vec![0], 100).into() ].into(); /// let assets_they_want: MultiAssetFilter = vec![ (Null, 200).into(), (vec![0], 50).into() [.into(); /// @@ -387,7 +387,7 @@ impl Assets { #[cfg(test)] mod tests { use super::*; - use xcm::v0::prelude::*; + use xcm::v1::prelude::*; use MultiLocation::Null; #[allow(non_snake_case)] fn AF(id: u8, amount: u128) -> MultiAsset { diff --git a/xcm/xcm-executor/src/config.rs b/xcm/xcm-executor/src/config.rs index ff39be56ef5e..1a38fb44ea50 100644 --- a/xcm/xcm-executor/src/config.rs +++ b/xcm/xcm-executor/src/config.rs @@ -22,7 +22,7 @@ use frame_support::{ dispatch::{Dispatchable, Parameter}, weights::{GetDispatchInfo, PostDispatchInfo}, }; -use xcm::v0::SendXcm; +use xcm::v1::SendXcm; /// The trait to parameterize the `XcmExecutor`. pub trait Config { diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index d6dcbd1f93fd..491558dbdb28 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -22,7 +22,7 @@ use frame_support::{ weights::GetDispatchInfo, }; use sp_std::{marker::PhantomData, prelude::*}; -use xcm::v0::{ +use xcm::v1::{ Error as XcmError, ExecuteXcm, MultiAssets, MultiLocation, Order, Outcome, Response, SendXcm, Xcm, }; diff --git a/xcm/xcm-executor/src/traits/conversion.rs b/xcm/xcm-executor/src/traits/conversion.rs index 49f5db698982..df99a05d0064 100644 --- a/xcm/xcm-executor/src/traits/conversion.rs +++ b/xcm/xcm-executor/src/traits/conversion.rs @@ -16,7 +16,7 @@ use parity_scale_codec::{Decode, Encode}; use sp_std::{borrow::Borrow, convert::TryFrom, prelude::*, result::Result}; -use xcm::v0::{MultiLocation, OriginKind}; +use xcm::v1::{MultiLocation, OriginKind}; /// Generic third-party conversion trait. Use this when you don't want to force the user to use default /// implementations of `From` and `Into` for the types you wish to convert between. @@ -139,7 +139,7 @@ impl Convert, T> for Decoded { /// which is passed to the next convert item. /// /// ```rust -/// # use xcm::v0::{MultiLocation, Junction, OriginKind}; +/// # use xcm::v1::{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; diff --git a/xcm/xcm-executor/src/traits/filter_asset_location.rs b/xcm/xcm-executor/src/traits/filter_asset_location.rs index 8b1a7bd1d1dc..a63af84e167c 100644 --- a/xcm/xcm-executor/src/traits/filter_asset_location.rs +++ b/xcm/xcm-executor/src/traits/filter_asset_location.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use xcm::v0::{MultiAsset, MultiLocation}; +use xcm::v1::{MultiAsset, MultiLocation}; /// Filters assets/location pairs. /// diff --git a/xcm/xcm-executor/src/traits/matches_fungible.rs b/xcm/xcm-executor/src/traits/matches_fungible.rs index 6634d16d0243..ff466af5b21c 100644 --- a/xcm/xcm-executor/src/traits/matches_fungible.rs +++ b/xcm/xcm-executor/src/traits/matches_fungible.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use xcm::v0::MultiAsset; +use xcm::v1::MultiAsset; pub trait MatchesFungible { fn matches_fungible(a: &MultiAsset) -> Option; diff --git a/xcm/xcm-executor/src/traits/matches_fungibles.rs b/xcm/xcm-executor/src/traits/matches_fungibles.rs index c1722d000f02..2499eae27531 100644 --- a/xcm/xcm-executor/src/traits/matches_fungibles.rs +++ b/xcm/xcm-executor/src/traits/matches_fungibles.rs @@ -15,7 +15,7 @@ // along with Polkadot. If not, see . use sp_std::result; -use xcm::v0::{Error as XcmError, MultiAsset}; +use xcm::v1::{Error as XcmError, MultiAsset}; /// Errors associated with [`MatchesFungibles`] operation. pub enum Error { diff --git a/xcm/xcm-executor/src/traits/on_response.rs b/xcm/xcm-executor/src/traits/on_response.rs index 134891b4b704..801aa016d478 100644 --- a/xcm/xcm-executor/src/traits/on_response.rs +++ b/xcm/xcm-executor/src/traits/on_response.rs @@ -15,7 +15,7 @@ // along with Polkadot. If not, see . use frame_support::weights::Weight; -use xcm::v0::{MultiLocation, Response}; +use xcm::v1::{MultiLocation, Response}; /// Define what needs to be done upon receiving a query response. pub trait OnResponse { diff --git a/xcm/xcm-executor/src/traits/should_execute.rs b/xcm/xcm-executor/src/traits/should_execute.rs index ed92866146c1..a8d9ad1ca79a 100644 --- a/xcm/xcm-executor/src/traits/should_execute.rs +++ b/xcm/xcm-executor/src/traits/should_execute.rs @@ -16,7 +16,7 @@ use frame_support::weights::Weight; use sp_std::result::Result; -use xcm::v0::{MultiLocation, Xcm}; +use xcm::v1::{MultiLocation, Xcm}; /// Trait to determine whether the execution engine should actually execute a given XCM. /// diff --git a/xcm/xcm-executor/src/traits/transact_asset.rs b/xcm/xcm-executor/src/traits/transact_asset.rs index d6114efbe09c..60fb0508c676 100644 --- a/xcm/xcm-executor/src/traits/transact_asset.rs +++ b/xcm/xcm-executor/src/traits/transact_asset.rs @@ -16,7 +16,7 @@ use crate::Assets; use sp_std::result::Result; -use xcm::v0::{Error as XcmError, MultiAsset, MultiLocation, Result as XcmResult}; +use xcm::v1::{Error as XcmError, MultiAsset, MultiLocation, Result as XcmResult}; /// Facility for asset transacting. /// diff --git a/xcm/xcm-executor/src/traits/weight.rs b/xcm/xcm-executor/src/traits/weight.rs index 4e9c805edfbd..c32ec682857c 100644 --- a/xcm/xcm-executor/src/traits/weight.rs +++ b/xcm/xcm-executor/src/traits/weight.rs @@ -17,7 +17,7 @@ use crate::Assets; use frame_support::weights::Weight; use sp_std::result::Result; -use xcm::v0::{Error, MultiAsset, MultiLocation, Xcm}; +use xcm::v1::{Error, MultiAsset, MultiLocation, Xcm}; /// Determine the weight of an XCM message. pub trait WeightBounds { diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index f318409bf187..00c1f4109421 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -100,7 +100,7 @@ mod tests { use codec::Encode; use frame_support::assert_ok; - use xcm::v0::{ + use xcm::v1::{ Junction::{self, Parachain, Parent}, MultiAsset::*, MultiLocation::*, diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index c69f20d05eaf..cd84249ad614 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -26,7 +26,7 @@ use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; use polkadot_parachain::primitives::Id as ParaId; use polkadot_runtime_parachains::{configuration, origin, shared, ump}; -use xcm::v0::{MultiAsset, MultiLocation, NetworkId}; +use xcm::v1::{MultiAsset, MultiLocation, NetworkId}; use xcm_builder::{ AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, From 00ca37b154ed1abefc9aaca907e5e08c0bb0a7e2 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 3 Aug 2021 22:38:08 +0200 Subject: [PATCH 54/84] Spelling --- xcm/xcm-simulator/example/src/parachain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index f4ad471ff697..781b0a8e6a45 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -187,7 +187,7 @@ pub mod mock_msg_queue { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { // XCMP - /// Some XCM was executed ok. + /// Some XCM was executed OK. Success(Option), /// Some XCM failed. Fail(Option, XcmError), From 768c96ae581e41a588e450a010ecfaeac88db372 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 3 Aug 2021 22:46:24 +0200 Subject: [PATCH 55/84] warnings --- xcm/src/lib.rs | 8 +++++--- xcm/src/v1/mod.rs | 4 ++-- xcm/src/v1/multilocation.rs | 3 +-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/xcm/src/lib.rs b/xcm/src/lib.rs index 705b4d34933b..c3b9a06ba141 100644 --- a/xcm/src/lib.rs +++ b/xcm/src/lib.rs @@ -32,7 +32,9 @@ pub mod v1; mod double_encoded; pub use double_encoded::DoubleEncoded; +#[derive(Clone, Eq, PartialEq, Debug)] pub enum Unsupported {} +impl Encode for Unsupported {} impl Decode for Unsupported { fn decode(_: &mut I) -> Result { Err("Not decodable".into()) @@ -51,7 +53,7 @@ pub enum VersionedXcm { impl From> for VersionedXcm { fn from(x: v1::Xcm) -> Self { - VersionedXcm::V0(x) + VersionedXcm::V1(x) } } @@ -68,9 +70,9 @@ impl TryFrom> for v1::Xcm { pub mod opaque { pub mod v1 { // Everything from v0 - pub use crate::v0::*; + pub use crate::v1::*; // Then override with the opaque types in v0 - pub use crate::v0::opaque::{Order, Xcm}; + pub use crate::v1::opaque::{Order, Xcm}; } /// The basic `VersionedXcm` type which just uses the `Vec` as an encoded call. diff --git a/xcm/src/v1/mod.rs b/xcm/src/v1/mod.rs index eb97acdeebde..38e7cf2c60ce 100644 --- a/xcm/src/v1/mod.rs +++ b/xcm/src/v1/mod.rs @@ -16,9 +16,9 @@ //! Version 0 of the Cross-Consensus Message format data structures. -use crate::{DoubleEncoded, VersionedXcm}; +use crate::DoubleEncoded; use alloc::vec::Vec; -use core::{convert::TryFrom, fmt::Debug, result}; +use core::fmt::Debug; use derivative::Derivative; use parity_scale_codec::{self, Decode, Encode}; diff --git a/xcm/src/v1/multilocation.rs b/xcm/src/v1/multilocation.rs index af1b289702b2..b3a7209404cc 100644 --- a/xcm/src/v1/multilocation.rs +++ b/xcm/src/v1/multilocation.rs @@ -16,10 +16,9 @@ //! Cross-Consensus Message format data structures. -use core::{convert::TryFrom, mem, result}; +use core::{mem, result}; use super::Junction; -use crate::VersionedMultiLocation; use parity_scale_codec::{self, Decode, Encode}; /// A relative path between state-bearing consensus systems. From 2936419d834f6159ac3c68a62ed3bc5fb7ea9694 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 3 Aug 2021 22:52:13 +0200 Subject: [PATCH 56/84] Replace some more v0->v1s --- runtime/common/src/xcm_sender.rs | 2 +- runtime/parachains/src/hrmp.rs | 6 +++--- runtime/parachains/src/ump.rs | 2 +- runtime/westend/src/constants.rs | 2 +- xcm/pallet-xcm/src/mock.rs | 6 +++--- xcm/pallet-xcm/src/tests.rs | 4 ++-- xcm/src/v1/multilocation.rs | 2 +- xcm/xcm-simulator/example/src/parachain.rs | 2 +- xcm/xcm-simulator/src/lib.rs | 2 +- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/runtime/common/src/xcm_sender.rs b/runtime/common/src/xcm_sender.rs index 151c649a8c4c..819f9b0b0617 100644 --- a/runtime/common/src/xcm_sender.rs +++ b/runtime/common/src/xcm_sender.rs @@ -20,7 +20,7 @@ use parity_scale_codec::Encode; use runtime_parachains::{configuration, dmp}; use sp_std::marker::PhantomData; use xcm::opaque::{ - v0::{Error, Junction, MultiLocation, Result, SendXcm, Xcm}, + v1::{Error, Junction, MultiLocation, Result, SendXcm, Xcm}, VersionedXcm, }; diff --git a/runtime/parachains/src/hrmp.rs b/runtime/parachains/src/hrmp.rs index 906c9d77d39d..901be0ae4b3b 100644 --- a/runtime/parachains/src/hrmp.rs +++ b/runtime/parachains/src/hrmp.rs @@ -1007,7 +1007,7 @@ impl Pallet { let notification_bytes = { use parity_scale_codec::Encode as _; - use xcm::opaque::{v0::Xcm, VersionedXcm}; + use xcm::opaque::{v1::Xcm, VersionedXcm}; VersionedXcm::from(Xcm::HrmpNewChannelOpenRequest { sender: u32::from(origin), @@ -1066,7 +1066,7 @@ impl Pallet { let notification_bytes = { use parity_scale_codec::Encode as _; - use xcm::opaque::{v0::Xcm, VersionedXcm}; + use xcm::opaque::{v1::Xcm, VersionedXcm}; VersionedXcm::from(Xcm::HrmpChannelAccepted { recipient: u32::from(origin) }).encode() }; @@ -1106,7 +1106,7 @@ impl Pallet { let config = >::config(); let notification_bytes = { use parity_scale_codec::Encode as _; - use xcm::opaque::{v0::Xcm, VersionedXcm}; + use xcm::opaque::{v1::Xcm, VersionedXcm}; VersionedXcm::from(Xcm::HrmpChannelClosing { initiator: u32::from(origin), diff --git a/runtime/parachains/src/ump.rs b/runtime/parachains/src/ump.rs index 32e7edadaf79..b1a39405a468 100644 --- a/runtime/parachains/src/ump.rs +++ b/runtime/parachains/src/ump.rs @@ -85,7 +85,7 @@ impl, C: Config> UmpSink for XcmSink Result { use xcm::{ - v0::{Error as XcmError, Junction, MultiLocation, Xcm}, + v1::{Error as XcmError, Junction, MultiLocation, Xcm}, VersionedXcm, }; diff --git a/runtime/westend/src/constants.rs b/runtime/westend/src/constants.rs index 77bdd954c03b..a6eaba9e2063 100644 --- a/runtime/westend/src/constants.rs +++ b/runtime/westend/src/constants.rs @@ -49,7 +49,7 @@ pub mod fee { use frame_support::weights::{ WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, }; - use primitives::v0::Balance; + use primitives::v1::Balance; use runtime_common::ExtrinsicBaseWeight; use smallvec::smallvec; pub use sp_runtime::Perbill; diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index eb9136b4ed03..e919be21a21d 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -25,8 +25,8 @@ use sp_core::H256; use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; pub use sp_std::{cell::RefCell, fmt::Debug, marker::PhantomData}; use xcm::{ - opaque::v0::{Error as XcmError, MultiAsset, Result as XcmResult, SendXcm, Xcm}, - v0::{MultiLocation, NetworkId, Order}, + opaque::v1::{Error as XcmError, MultiAsset, Result as XcmResult, SendXcm, Xcm}, + v1::{MultiLocation, NetworkId, Order}, }; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, @@ -195,7 +195,7 @@ pub(crate) fn last_event() -> Event { } pub(crate) fn buy_execution(debt: Weight) -> Order { - use xcm::opaque::v0::prelude::*; + use xcm::opaque::v1::prelude::*; Order::BuyExecution { fees: All, weight: 0, debt, halt_on_error: false, xcm: vec![] } } diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index f0727e619bd2..2fc2de28ef64 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -18,8 +18,8 @@ use crate::mock::*; use frame_support::{assert_noop, assert_ok, traits::Currency}; use polkadot_parachain::primitives::{AccountIdConversion, Id as ParaId}; use xcm::{ - opaque::v0::prelude::*, - v0::{Junction, Xcm}, + opaque::v1::prelude::*, + v1::{Junction, Xcm}, }; const ALICE: AccountId = AccountId::new([0u8; 32]); diff --git a/xcm/src/v1/multilocation.rs b/xcm/src/v1/multilocation.rs index b3a7209404cc..20a9153b570a 100644 --- a/xcm/src/v1/multilocation.rs +++ b/xcm/src/v1/multilocation.rs @@ -699,7 +699,7 @@ impl MultiLocation { #[cfg(test)] mod tests { use super::MultiLocation::*; - use crate::opaque::v0::{Junction::*, NetworkId::Any}; + use crate::opaque::v1::{Junction::*, NetworkId::Any}; #[test] fn match_and_split_works() { diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index 781b0a8e6a45..993aee5de0ca 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -36,7 +36,7 @@ use polkadot_parachain::primitives::{ DmpMessageHandler, Id as ParaId, Sibling, XcmpMessageFormat, XcmpMessageHandler, }; use xcm::{ - v0::{ + v1::{ Error as XcmError, ExecuteXcm, Junction::{Parachain, Parent}, MultiAsset, diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index d68072858e80..f075c7e3d9fb 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -32,7 +32,7 @@ pub use polkadot_runtime_parachains::{ dmp, ump::{self, MessageId, UmpSink, XcmSink}, }; -pub use xcm::{v0::prelude::*, VersionedXcm}; +pub use xcm::{v1::prelude::*, VersionedXcm}; pub use xcm_executor::XcmExecutor; pub trait TestExt { From 015fff2e1ba83d42f2897e2da39ff4985facffbe Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 3 Aug 2021 23:00:39 +0200 Subject: [PATCH 57/84] warnings --- xcm/xcm-simulator/example/src/parachain.rs | 17 ++++------------- xcm/xcm-simulator/example/src/relay_chain.rs | 8 ++++---- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index 993aee5de0ca..4e870abb0315 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -35,19 +35,10 @@ use polkadot_core_primitives::BlockNumber as RelayBlockNumber; use polkadot_parachain::primitives::{ DmpMessageHandler, Id as ParaId, Sibling, XcmpMessageFormat, XcmpMessageHandler, }; -use xcm::{ - v1::{ - Error as XcmError, ExecuteXcm, - Junction::{Parachain, Parent}, - MultiAsset, - MultiLocation::{self, X1}, - NetworkId, Outcome, Xcm, - }, - VersionedXcm, -}; +use xcm::{v1::prelude::*, VersionedXcm}; use xcm_builder::{ AccountId32Aliases, AllowUnpaidExecutionFrom, CurrencyAdapter as XcmCurrencyAdapter, - EnsureXcmOrigin, FixedRateOfConcreteFungible, FixedWeightBounds, IsConcrete, LocationInverter, + EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset, ParentIsDefault, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, }; @@ -129,7 +120,7 @@ pub type XcmOriginToCallOrigin = ( parameter_types! { pub const UnitWeightCost: Weight = 1; - pub KsmPerSecond: (MultiLocation, u128) = (X1(Parent), 1); + pub KsmPerSecond: (AssetId, u128) = (Concrete(X1(Parent)), 1); } pub type LocalAssetTransactor = @@ -149,7 +140,7 @@ impl Config for XcmConfig { type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; - type Trader = FixedRateOfConcreteFungible; + type Trader = FixedRateOfFungible; type ResponseHandler = (); } diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index cd84249ad614..b342ebaf7fff 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -26,11 +26,11 @@ use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; use polkadot_parachain::primitives::Id as ParaId; use polkadot_runtime_parachains::{configuration, origin, shared, ump}; -use xcm::v1::{MultiAsset, MultiLocation, NetworkId}; +use xcm::v1::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, - CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfConcreteFungible, FixedWeightBounds, + CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, }; @@ -114,7 +114,7 @@ type LocalOriginConverter = ( parameter_types! { pub const BaseXcmWeight: Weight = 1_000; - pub KsmPerSecond: (MultiLocation, u128) = (KsmLocation::get(), 1); + pub KsmPerSecond: (AssetId, u128) = (Concrete(KsmLocation::get()), 1); } pub type XcmRouter = super::RelayChainXcmRouter; @@ -131,7 +131,7 @@ impl Config for XcmConfig { type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; - type Trader = FixedRateOfConcreteFungible; + type Trader = FixedRateOfFungible; type ResponseHandler = (); } From b5fe03e83a967793618e737dbb8caec8c1eb1e57 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 3 Aug 2021 23:06:01 +0200 Subject: [PATCH 58/84] format --- xcm/src/lib.rs | 4 ++-- xcm/src/v1/mod.rs | 6 +++--- xcm/xcm-simulator/example/src/relay_chain.rs | 5 ++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/xcm/src/lib.rs b/xcm/src/lib.rs index c3b9a06ba141..f9ce3a3f5266 100644 --- a/xcm/src/lib.rs +++ b/xcm/src/lib.rs @@ -23,9 +23,9 @@ #![no_std] extern crate alloc; -use core::{result::Result, convert::TryFrom}; +use core::{convert::TryFrom, result::Result}; use derivative::Derivative; -use parity_scale_codec::{Decode, Encode, Input, Error as CodecError}; +use parity_scale_codec::{Decode, Encode, Error as CodecError, Input}; pub mod v1; diff --git a/xcm/src/v1/mod.rs b/xcm/src/v1/mod.rs index 38e7cf2c60ce..375a8d8a3cfc 100644 --- a/xcm/src/v1/mod.rs +++ b/xcm/src/v1/mod.rs @@ -23,17 +23,17 @@ use derivative::Derivative; use parity_scale_codec::{self, Decode, Encode}; mod junction; -mod multilocation; pub mod multiasset; +mod multilocation; mod order; mod traits; // the new multiasset. pub use junction::{BodyId, BodyPart, Junction, NetworkId}; -pub use multilocation::MultiLocation; pub use multiasset::{ AssetId, AssetInstance, Fungibility, MultiAsset, MultiAssetFilter, MultiAssets, WildFungibility, WildMultiAsset, }; +pub use multilocation::MultiLocation; pub use order::Order; pub use traits::{Error, ExecuteXcm, Outcome, Result, SendXcm}; @@ -45,7 +45,6 @@ pub mod prelude { Junction::*, NetworkId::{self, *}, }, - multilocation::MultiLocation::{self, *}, multiasset::{ AssetId::{self, *}, AssetInstance::{self, *}, @@ -56,6 +55,7 @@ pub mod prelude { WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, WildMultiAsset::{self, *}, }, + multilocation::MultiLocation::{self, *}, opaque, order::Order::{self, *}, traits::{Error as XcmError, ExecuteXcm, Outcome, Result as XcmResult, SendXcm}, diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index b342ebaf7fff..7472df198219 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -30,9 +30,8 @@ use xcm::v1::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, - CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, - IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, + CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete, + LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, }; use xcm_executor::{Config, XcmExecutor}; From 608a212fa9ed7ddc1bde53855214a3323bcee5d7 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 3 Aug 2021 23:30:51 +0200 Subject: [PATCH 59/84] Add max_assets param --- xcm/src/v1/order.rs | 12 ++++----- xcm/xcm-executor/src/assets.rs | 45 +++++++++++++++++++++++++++++++--- xcm/xcm-executor/src/lib.rs | 8 +++--- 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/xcm/src/v1/order.rs b/xcm/src/v1/order.rs index aed5a6ff3634..f5ba0e65bc48 100644 --- a/xcm/src/v1/order.rs +++ b/xcm/src/v1/order.rs @@ -39,8 +39,7 @@ pub enum Order { /// /// Errors: #[codec(index = 1)] - // TODO: https://github.com/paritytech/polkadot/issues/3547 introduce `, max_assets: u32` - DepositAsset { assets: MultiAssetFilter, dest: MultiLocation }, + DepositAsset { assets: MultiAssetFilter, max_assets: u32, dest: MultiLocation }, /// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within /// this consensus system. @@ -54,8 +53,7 @@ pub enum Order { /// /// Errors: #[codec(index = 2)] - // TODO: https://github.com/paritytech/polkadot/issues/3547 introduce `, max_assets: u32` - DepositReserveAsset { assets: MultiAssetFilter, dest: MultiLocation, effects: Vec> }, + DepositReserveAsset { assets: MultiAssetFilter, max_assets: u32, dest: MultiLocation, effects: Vec> }, /// Remove the asset(s) (`give`) from holding and replace them with alternative assets. /// @@ -139,9 +137,9 @@ impl Order { use Order::*; match order { Noop => Noop, - DepositAsset { assets, dest } => DepositAsset { assets, dest }, - DepositReserveAsset { assets, dest, effects } => - DepositReserveAsset { assets, dest, effects }, + DepositAsset { assets, max_assets, dest } => DepositAsset { assets, max_assets, dest }, + DepositReserveAsset { assets, max_assets, dest, effects } => + DepositReserveAsset { assets, max_assets, dest, effects }, ExchangeAsset { give, receive } => ExchangeAsset { give, receive }, InitiateReserveWithdraw { assets, reserve, effects } => InitiateReserveWithdraw { assets, reserve, effects }, diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 5270a30d44d2..f2354e058165 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -89,6 +89,11 @@ impl Assets { Self::default() } + /// Total number of distinct assets. + pub fn len(&self) -> usize { + self.fungible.len() + self.non_fungible.len() + } + /// A borrowing iterator over the fungible assets. pub fn fungible_assets_iter<'a>(&'a self) -> impl Iterator + 'a { self.fungible @@ -231,10 +236,30 @@ impl Assets { &mut self, mask: MultiAssetFilter, saturate: bool, + limit: usize, ) -> Result { let mut taken = Assets::new(); match mask { - MultiAssetFilter::Wild(All) => return Ok(self.swapped(Assets::new())), + MultiAssetFilter::Wild(All) => if self.fungible.len() + self.non_fungible.len() <= limit { + return Ok(self.swapped(Assets::new())) + } else { + let fungible = mem::replace(&mut self.fungible, Default::default()); + fungible.into_iter().for_each(|(c, amount)| { + if taken.len() < limit { + taken.fungible.insert(c, amount); + } else { + self.fungible.insert(c, amount); + } + }); + let non_fungible = mem::replace(&mut self.non_fungible, Default::default()); + non_fungible.into_iter().for_each(|(c, instance)| { + if taken.len() < limit { + taken.non_fungible.insert((c, instance)); + } else { + self.non_fungible.insert((c, instance)); + } + }); + }, MultiAssetFilter::Wild(AllOf { fun: WildFungible, id }) => { if let Some((id, amount)) = self.fungible.remove_entry(&id) { taken.fungible.insert(id, amount); @@ -243,7 +268,7 @@ impl Assets { MultiAssetFilter::Wild(AllOf { fun: WildNonFungible, id }) => { let non_fungible = mem::replace(&mut self.non_fungible, Default::default()); non_fungible.into_iter().for_each(|(c, instance)| { - if c == id { + if c == id && taken.len() < limit { taken.non_fungible.insert((c, instance)); } else { self.non_fungible.insert((c, instance)); @@ -279,6 +304,9 @@ impl Assets { } }, } + if taken.len() == limit { + break + } } }, } @@ -290,7 +318,16 @@ impl Assets { /// Returns `Ok` with the non-wildcard equivalence of `mask` taken and mutates `self` to its value minus /// `mask` if `self` contains `asset`, and return `Err` otherwise. pub fn saturating_take(&mut self, asset: MultiAssetFilter) -> Assets { - self.general_take(asset, true) + self.general_take(asset, true, usize::max_value()) + .expect("general_take never results in error when saturating") + } + + /// Mutates `self` to its original value less `mask` and returns `true` iff it contains at least `mask`. + /// + /// Returns `Ok` with the non-wildcard equivalence of `mask` taken and mutates `self` to its value minus + /// `mask` if `self` contains `asset`, and return `Err` otherwise. + pub fn limited_saturating_take(&mut self, asset: MultiAssetFilter, limit: usize) -> Assets { + self.general_take(asset, true, limit) .expect("general_take never results in error when saturating") } @@ -299,7 +336,7 @@ impl Assets { /// Returns `Ok` with the non-wildcard equivalence of `asset` taken and mutates `self` to its value minus /// `asset` if `self` contains `asset`, and return `Err` otherwise. pub fn try_take(&mut self, mask: MultiAssetFilter) -> Result { - self.general_take(mask, false) + self.general_take(mask, false, usize::max_value()) } /// Consumes `self` and returns its original value excluding `asset` iff it contains at least `asset`. diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 491558dbdb28..46be5ae644df 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -269,14 +269,14 @@ impl XcmExecutor { ); let mut total_surplus = 0; match effect { - Order::DepositAsset { assets, dest } => { - let deposited = holding.saturating_take(assets); + Order::DepositAsset { assets, max_assets, dest } => { + let deposited = holding.limited_saturating_take(assets, max_assets as usize); for asset in deposited.into_assets_iter() { Config::AssetTransactor::deposit_asset(&asset, &dest)?; } }, - Order::DepositReserveAsset { assets, dest, effects } => { - let deposited = holding.saturating_take(assets); + Order::DepositReserveAsset { assets, max_assets, dest, effects } => { + let deposited = holding.limited_saturating_take(assets, max_assets as usize); for asset in deposited.assets_iter() { Config::AssetTransactor::deposit_asset(&asset, &dest)?; } From dee3be53d9c03ce2e09a974130d7870c7a07637c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 3 Aug 2021 23:37:49 +0200 Subject: [PATCH 60/84] building --- xcm/pallet-xcm/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index ece2590f4d53..38293257c21d 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -165,7 +165,7 @@ pub mod pallet { halt_on_error: false, xcm: vec![], }, - DepositAsset { assets: Wild(All), dest: beneficiary }, + DepositAsset { assets: Wild(All), max_assets: 1, dest: beneficiary }, ], }], }; @@ -222,7 +222,7 @@ pub mod pallet { halt_on_error: false, xcm: vec![], }, - DepositAsset { assets: Wild(All), dest: beneficiary }, + DepositAsset { assets: Wild(All), max_assets: 1, dest: beneficiary }, ], }; let weight = From a6eaaa2a99ee5800a9c7a9f09535656208826f7e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 3 Aug 2021 23:50:02 +0200 Subject: [PATCH 61/84] test fixes --- xcm/pallet-xcm/src/tests.rs | 24 ++++++++++++------------ xcm/src/v1/multiasset.rs | 8 +++++++- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index 2fc2de28ef64..da5d4405cc4a 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -38,12 +38,12 @@ fn send_works() { new_test_ext_with_balances(balances).execute_with(|| { let weight = 2 * BaseXcmWeight::get(); let sender: MultiLocation = - Junction::AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(); - let message = Xcm::ReserveAssetDeposit { - assets: vec![ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT }], + AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(); + let message = Xcm::ReserveAssetDeposited { + assets: (X1(Parent), SEND_AMOUNT).into(), effects: vec![ buy_execution(weight), - DepositAsset { assets: vec![All], dest: sender.clone() }, + DepositAsset { assets: Wild(All), dest: sender.clone() }, ], }; assert_ok!(XcmPallet::send(Origin::signed(ALICE), RelayLocation::get(), message.clone())); @@ -77,7 +77,7 @@ fn send_fails_when_xcm_router_blocks() { assets: vec![ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT }], effects: vec![ buy_execution(weight), - DepositAsset { assets: vec![All], dest: sender.clone() }, + DepositAsset { assets: Wild(All), dest: sender.clone() }, ], }; assert_noop!( @@ -114,8 +114,8 @@ fn teleport_assets_works() { assert_ok!(XcmPallet::teleport_assets( Origin::signed(ALICE), RelayLocation::get(), - MultiLocation::X1(Junction::AccountId32 { network: NetworkId::Any, id: BOB.into() }), - vec![ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT }], + X1(AccountId32 { network: Any, id: BOB.into() }), + (Null, SEND_AMOUNT).into(), weight, )); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT); @@ -143,7 +143,7 @@ fn reserve_transfer_assets_works() { Origin::signed(ALICE), Parachain(PARA_ID).into(), dest.clone(), - vec![ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT }], + (Null, SEND_AMOUNT).into(), weight )); // Alice spent amount @@ -156,8 +156,8 @@ fn reserve_transfer_assets_works() { vec![( Parachain(PARA_ID).into(), Xcm::ReserveAssetDeposit { - assets: vec![ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT }], - effects: vec![buy_execution(weight), DepositAsset { assets: vec![All], dest },] + assets: (X1(Parent), SEND_AMOUNT).into(), + effects: vec![buy_execution(weight), DepositAsset { assets: Wild(All), dest },] } )] ); @@ -184,8 +184,8 @@ fn execute_withdraw_to_deposit_works() { assert_ok!(XcmPallet::execute( Origin::signed(ALICE), Box::new(Xcm::WithdrawAsset { - assets: vec![ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT }], - effects: vec![buy_execution(weight), DepositAsset { assets: vec![All], dest }], + assets: (Null, SEND_AMOUNT).into(), + effects: vec![buy_execution(weight), DepositAsset { assets: Wild(All), dest }], }), weight )); diff --git a/xcm/src/v1/multiasset.rs b/xcm/src/v1/multiasset.rs index c11110ed571c..572146ad34ad 100644 --- a/xcm/src/v1/multiasset.rs +++ b/xcm/src/v1/multiasset.rs @@ -23,7 +23,7 @@ //! - `MultiAssetFilter`: A combination of `Wild` and `MultiAssets` designed for efficiently filtering an XCM holding //! account. -use super::MultiLocation; +use super::{Junction, MultiLocation::{self, X1}}; use alloc::{vec, vec::Vec}; use core::cmp::Ordering; use parity_scale_codec::{self as codec, Decode, Encode}; @@ -70,6 +70,12 @@ impl From for AssetId { } } +impl From for AssetId { + fn from(x: Junction) -> Self { + Self::Concrete(X1(x)) + } +} + impl From> for AssetId { fn from(x: Vec) -> Self { Self::Abstract(x) From f58a65931f9aa606c01e1aadacd1b8c7d3480996 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 3 Aug 2021 23:59:12 +0200 Subject: [PATCH 62/84] tests --- xcm/pallet-xcm/src/mock.rs | 10 +++++----- xcm/pallet-xcm/src/tests.rs | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index e919be21a21d..b347b3520882 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -26,12 +26,12 @@ use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; pub use sp_std::{cell::RefCell, fmt::Debug, marker::PhantomData}; use xcm::{ opaque::v1::{Error as XcmError, MultiAsset, Result as XcmResult, SendXcm, Xcm}, - v1::{MultiLocation, NetworkId, Order}, + v1::prelude::*, }; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, - CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfConcreteFungible, FixedWeightBounds, + CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, }; @@ -154,7 +154,7 @@ type LocalOriginConverter = ( parameter_types! { pub const BaseXcmWeight: Weight = 1_000; - pub CurrencyPerSecond: (MultiLocation, u128) = (RelayLocation::get(), 1); + pub CurrencyPerSecond: (AssetId, u128) = (Concrete(RelayLocation::get()), 1); } pub type Barrier = (TakeWeightCredit, AllowTopLevelPaidExecutionFrom>); @@ -170,7 +170,7 @@ impl xcm_executor::Config for XcmConfig { type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; - type Trader = FixedRateOfConcreteFungible; + type Trader = FixedRateOfFungible; type ResponseHandler = (); } @@ -196,7 +196,7 @@ pub(crate) fn last_event() -> Event { pub(crate) fn buy_execution(debt: Weight) -> Order { use xcm::opaque::v1::prelude::*; - Order::BuyExecution { fees: All, weight: 0, debt, halt_on_error: false, xcm: vec![] } + Order::BuyExecution { fees: (RelayLocation::get(), 1).into(), weight: 0, debt, halt_on_error: false, xcm: vec![] } } pub(crate) fn new_test_ext_with_balances( diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index da5d4405cc4a..6c1a7c6203cc 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -73,8 +73,8 @@ fn send_fails_when_xcm_router_blocks() { let weight = 2 * BaseXcmWeight::get(); let sender: MultiLocation = Junction::AccountId32 { network: AnyNetwork::get(), id: ALICE.into() }.into(); - let message = Xcm::ReserveAssetDeposit { - assets: vec![ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT }], + let message = Xcm::ReserveAssetDeposited { + assets: (Parent, SEND_AMOUNT).into(), effects: vec![ buy_execution(weight), DepositAsset { assets: Wild(All), dest: sender.clone() }, @@ -155,7 +155,7 @@ fn reserve_transfer_assets_works() { sent_xcm(), vec![( Parachain(PARA_ID).into(), - Xcm::ReserveAssetDeposit { + Xcm::ReserveAssetDeposited { assets: (X1(Parent), SEND_AMOUNT).into(), effects: vec![buy_execution(weight), DepositAsset { assets: Wild(All), dest },] } From 8a7810001e4583ccf789ca89733aedeeed75459a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 00:03:38 +0200 Subject: [PATCH 63/84] another test --- xcm/xcm-builder/src/weight.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xcm/xcm-builder/src/weight.rs b/xcm/xcm-builder/src/weight.rs index 87b7c2f6b488..028ce6b061e4 100644 --- a/xcm/xcm-builder/src/weight.rs +++ b/xcm/xcm-builder/src/weight.rs @@ -162,6 +162,9 @@ impl, R: TakeRevenue> WeightTrader for FixedRateOfFungib let (id, units_per_second) = T::get(); use frame_support::weights::constants::WEIGHT_PER_SECOND; let amount = units_per_second * (weight as u128) / (WEIGHT_PER_SECOND as u128); + if amount == 0 { + return Ok(payment) + } let unused = payment.checked_sub((id, amount).into()).map_err(|_| Error::TooExpensive)?; self.0 = self.0.saturating_add(weight); self.1 = self.1.saturating_add(amount); From 6eb75ad84691b42fa1898b93279f82296c66a047 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 00:04:55 +0200 Subject: [PATCH 64/84] final test --- xcm/pallet-xcm/src/mock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index b347b3520882..1051a171c06a 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -196,7 +196,7 @@ pub(crate) fn last_event() -> Event { pub(crate) fn buy_execution(debt: Weight) -> Order { use xcm::opaque::v1::prelude::*; - Order::BuyExecution { fees: (RelayLocation::get(), 1).into(), weight: 0, debt, halt_on_error: false, xcm: vec![] } + Order::BuyExecution { fees: (RelayLocation::get(), 10).into(), weight: 0, debt, halt_on_error: false, xcm: vec![] } } pub(crate) fn new_test_ext_with_balances( From 1302cc5bf7f13d0f70ef3d4cd0b3ea3a4f14d138 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Wed, 4 Aug 2021 09:50:45 +0200 Subject: [PATCH 65/84] fix names as per suggestion --- xcm/xcm-simulator/example/src/lib.rs | 26 +++++++++++++------------- xcm/xcm-simulator/src/lib.rs | 22 +++++++++++----------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 8dabff9809f5..e519670c5f53 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -127,7 +127,7 @@ mod tests { let remark = parachain::Call::System( frame_system::Call::::remark_with_event(vec![1, 2, 3]), ); - Relay::execute_and_dispatch_xcm(|| { + Relay::execute_with(|| { assert_ok!(RelayChainPalletXcm::send_xcm( Null, X1(Parachain(1)), @@ -139,7 +139,7 @@ mod tests { )); }); - ParaA::execute_and_dispatch_xcm(|| { + ParaA::execute_with(|| { use parachain::{Event, System}; assert!(System::events() .iter() @@ -154,7 +154,7 @@ mod tests { let remark = relay_chain::Call::System( frame_system::Call::::remark_with_event(vec![1, 2, 3]), ); - ParaA::execute_and_dispatch_xcm(|| { + ParaA::execute_with(|| { assert_ok!(ParachainPalletXcm::send_xcm( Null, X1(Parent), @@ -166,7 +166,7 @@ mod tests { )); }); - Relay::execute_and_dispatch_xcm(|| { + Relay::execute_with(|| { use relay_chain::{Event, System}; assert!(System::events() .iter() @@ -181,7 +181,7 @@ mod tests { let remark = parachain::Call::System( frame_system::Call::::remark_with_event(vec![1, 2, 3]), ); - ParaA::execute_and_dispatch_xcm(|| { + ParaA::execute_with(|| { assert_ok!(ParachainPalletXcm::send_xcm( Null, X2(Parent, Parachain(2)), @@ -193,7 +193,7 @@ mod tests { )); }); - ParaB::execute_and_dispatch_xcm(|| { + ParaB::execute_with(|| { use parachain::{Event, System}; assert!(System::events() .iter() @@ -212,7 +212,7 @@ mod tests { let withdraw_amount = 123; let max_weight_for_execution = 10; - Relay::execute_and_dispatch_xcm(|| { + Relay::execute_with(|| { assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( relay_chain::Origin::signed(ALICE), X1(Parachain(1)), @@ -226,7 +226,7 @@ mod tests { ); }); - ParaA::execute_and_dispatch_xcm(|| { + ParaA::execute_with(|| { // Check message received let expected_message = ( X1(Parent), @@ -264,7 +264,7 @@ mod tests { let send_amount = 10; let weight_for_execution = 3 * relay_chain::BaseXcmWeight::get(); - ParaA::execute_and_dispatch_xcm(|| { + ParaA::execute_with(|| { let message = WithdrawAsset { assets: vec![ConcreteFungible { id: Null, amount: send_amount }], effects: vec![ @@ -276,7 +276,7 @@ mod tests { assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone())); }); - Relay::execute_and_dispatch_xcm(|| { + Relay::execute_with(|| { assert_eq!( relay_chain::Balances::free_balance(para_account_id(1)), INITIAL_BALANCE - send_amount @@ -299,7 +299,7 @@ mod tests { let query_id_set = 1234; // Send a message which fully succeeds on the relay chain - ParaA::execute_and_dispatch_xcm(|| { + ParaA::execute_with(|| { let message = WithdrawAsset { assets: vec![ConcreteFungible { id: Null, amount: send_amount }], effects: vec![ @@ -317,7 +317,7 @@ mod tests { }); // Check that transfer was executed - Relay::execute_and_dispatch_xcm(|| { + Relay::execute_with(|| { // Withdraw executed assert_eq!( relay_chain::Balances::free_balance(para_account_id(1)), @@ -328,7 +328,7 @@ mod tests { }); // Check that QueryResponse message was received - ParaA::execute_and_dispatch_xcm(|| { + ParaA::execute_with(|| { assert_eq!( parachain::MsgQueue::received_dmp(), vec![( diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 0bc596eeca0f..a8ec113cc25b 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -41,16 +41,16 @@ pub trait TestExt { fn reset_ext(); /// Execute code in the context of the test externalities, without automatic /// message processing. All messages in the message buses can be processed - /// by calling `Self::dispatch_xcm_queue()`. - fn execute_and_queue_xcm(execute: impl FnOnce() -> R) -> R; + /// by calling `Self::dispatch_xcm_buses()`. + fn execute_without_dispatch(execute: impl FnOnce() -> R) -> R; /// Process all messages in the message buses - fn dispatch_xcm_queue(); + fn dispatch_xcm_buses(); /// Execute some code in the context of the test externalities, with /// automatic message processing. /// Messages are dispatched once the passed closure completes. - fn execute_and_dispatch_xcm(execute: impl FnOnce() -> R) -> R { - let result = Self::execute_and_queue_xcm(execute); - Self::dispatch_xcm_queue(); + fn execute_with(execute: impl FnOnce() -> R) -> R { + let result = Self::execute_without_dispatch(execute); + Self::dispatch_xcm_buses(); result } } @@ -96,7 +96,7 @@ macro_rules! decl_test_relay_chain { ) -> Result<$crate::Weight, ($crate::MessageId, $crate::Weight)> { use $crate::{ump::UmpSink, TestExt}; - Self::execute_and_dispatch_xcm(|| { + Self::execute_with(|| { $crate::ump::XcmSink::<$crate::XcmExecutor<$xcm_config>, $runtime>::process_upward_message( origin, msg, max_weight, ) @@ -130,7 +130,7 @@ macro_rules! decl_test_parachain { ) -> $crate::Weight { use $crate::{TestExt, XcmpMessageHandlerT}; - $name::execute_and_dispatch_xcm(|| { + $name::execute_with(|| { <$xcmp_message_handler>::handle_xcmp_messages(iter, max_weight) }) } @@ -143,7 +143,7 @@ macro_rules! decl_test_parachain { ) -> $crate::Weight { use $crate::{DmpMessageHandlerT, TestExt}; - $name::execute_and_dispatch_xcm(|| { + $name::execute_with(|| { <$dmp_message_handler>::handle_dmp_messages(iter, max_weight) }) } @@ -175,11 +175,11 @@ macro_rules! __impl_ext { $ext_name.with(|v| *v.borrow_mut() = $new_ext); } - fn execute_and_queue_xcm(execute: impl FnOnce() -> R) -> R { + fn execute_without_dispatch(execute: impl FnOnce() -> R) -> R { $ext_name.with(|v| v.borrow_mut().execute_with(execute)) } - fn dispatch_xcm_queue() { + fn dispatch_xcm_buses() { while exists_messages_in_any_bus() { if let Err(xcm_error) = process_relay_messages() { panic!("Relay chain XCM execution failure: {:?}", xcm_error); From 9d0b9f9900070115363ae4fb5198acce109763b9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 11:56:32 +0200 Subject: [PATCH 66/84] tests --- xcm/pallet-xcm/src/mock.rs | 14 ++++++++++---- xcm/src/v1/multiasset.rs | 5 ++++- xcm/xcm-simulator/example/src/lib.rs | 10 ++-------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index 1051a171c06a..0fa4ba225ce2 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -31,9 +31,9 @@ use xcm::{ use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, - CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, - IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, + CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete, + LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, + TakeWeightCredit, }; use xcm_executor::XcmExecutor; @@ -196,7 +196,13 @@ pub(crate) fn last_event() -> Event { pub(crate) fn buy_execution(debt: Weight) -> Order { use xcm::opaque::v1::prelude::*; - Order::BuyExecution { fees: (RelayLocation::get(), 10).into(), weight: 0, debt, halt_on_error: false, xcm: vec![] } + Order::BuyExecution { + fees: (RelayLocation::get(), 10).into(), + weight: 0, + debt, + halt_on_error: false, + xcm: vec![], + } } pub(crate) fn new_test_ext_with_balances( diff --git a/xcm/src/v1/multiasset.rs b/xcm/src/v1/multiasset.rs index 572146ad34ad..5716a7b5c980 100644 --- a/xcm/src/v1/multiasset.rs +++ b/xcm/src/v1/multiasset.rs @@ -23,7 +23,10 @@ //! - `MultiAssetFilter`: A combination of `Wild` and `MultiAssets` designed for efficiently filtering an XCM holding //! account. -use super::{Junction, MultiLocation::{self, X1}}; +use super::{ + Junction, + MultiLocation::{self, X1}, +}; use alloc::{vec, vec::Vec}; use core::cmp::Ordering; use parity_scale_codec::{self as codec, Decode, Encode}; diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 00c1f4109421..978795a1546e 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -100,13 +100,7 @@ mod tests { use codec::Encode; use frame_support::assert_ok; - use xcm::v1::{ - Junction::{self, Parachain, Parent}, - MultiAsset::*, - MultiLocation::*, - NetworkId, OriginKind, - Xcm::*, - }; + use xcm::v1::prelude::*; use xcm_simulator::TestExt; #[test] @@ -199,7 +193,7 @@ mod tests { relay_chain::Origin::signed(ALICE), X1(Parachain(1)), X1(Junction::AccountId32 { network: NetworkId::Any, id: ALICE.into() }), - vec![ConcreteFungible { id: Null, amount: 123 }], + (Null, 123).into(), 123, )); }); From b5b63c8208dce9c29f3d9af9bffee57dc8739870 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 12:26:48 +0200 Subject: [PATCH 67/84] Rename Null -> Here --- runtime/kusama/src/lib.rs | 6 +-- runtime/rococo/src/lib.rs | 4 +- runtime/westend/src/lib.rs | 4 +- xcm/pallet-xcm/src/lib.rs | 8 ++-- xcm/pallet-xcm/src/mock.rs | 4 +- xcm/pallet-xcm/src/tests.rs | 16 +++---- xcm/src/v1/mod.rs | 29 ++++++------ xcm/src/v1/multilocation.rs | 40 ++++++++-------- xcm/src/v1/order.rs | 15 ++++-- xcm/xcm-builder/src/barriers.rs | 2 +- xcm/xcm-builder/src/mock.rs | 4 +- xcm/xcm-builder/src/origin_conversion.rs | 2 +- xcm/xcm-builder/src/tests.rs | 46 +++++++++---------- xcm/xcm-builder/src/weight.rs | 4 +- xcm/xcm-executor/src/assets.rs | 20 ++++---- xcm/xcm-executor/src/lib.rs | 12 ++--- xcm/xcm-executor/src/traits/transact_asset.rs | 10 ++-- xcm/xcm-simulator/example/src/lib.rs | 8 ++-- xcm/xcm-simulator/example/src/relay_chain.rs | 4 +- 19 files changed, 122 insertions(+), 116 deletions(-) diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index c2720c8deed1..6fc6b8ac3524 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -1199,14 +1199,14 @@ impl auctions::Config for Runtime { parameter_types! { /// The location of the KSM token, from the context of this chain. Since this token is native to this - /// chain, we make it synonymous with it and thus it is the `Null` location, which means "equivalent to + /// chain, we make it synonymous with it and thus it is the `Here` location, which means "equivalent to /// the context". - pub const KsmLocation: MultiLocation = MultiLocation::Null; + pub const KsmLocation: MultiLocation = MultiLocation::Here; /// The Kusama network ID. This is named. pub const KusamaNetwork: NetworkId = NetworkId::Kusama; /// Our XCM location ancestry - i.e. what, if anything, `Parent` means evaluated in our context. Since /// Kusama is a top-level relay-chain, there is no ancestry. - pub const Ancestry: MultiLocation = MultiLocation::Null; + pub const Ancestry: MultiLocation = MultiLocation::Here; /// The check account, which holds any native assets that have been teleported out and not back in (yet). pub CheckAccount: AccountId = XcmPallet::check_account(); } diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 88e6072003ff..f05de3d26771 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -583,9 +583,9 @@ impl parachains_paras::Config for Runtime { } parameter_types! { - pub const RocLocation: MultiLocation = MultiLocation::Null; + pub const RocLocation: MultiLocation = MultiLocation::Here; pub const RococoNetwork: NetworkId = NetworkId::Polkadot; - pub const Ancestry: MultiLocation = MultiLocation::Null; + pub const Ancestry: MultiLocation = MultiLocation::Here; pub CheckAccount: AccountId = XcmPallet::check_account(); } diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index a578ff548afc..d0304022c602 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -866,8 +866,8 @@ impl auctions::Config for Runtime { } parameter_types! { - pub const WndLocation: MultiLocation = MultiLocation::Null; - pub const Ancestry: MultiLocation = MultiLocation::Null; + pub const WndLocation: MultiLocation = MultiLocation::Here; + pub const Ancestry: MultiLocation = MultiLocation::Here; pub WestendNetwork: NetworkId = NetworkId::Named(b"Westend".to_vec()); pub CheckAccount: AccountId = XcmPallet::check_account(); } diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index ece2590f4d53..3486e9777a94 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -165,7 +165,7 @@ pub mod pallet { halt_on_error: false, xcm: vec![], }, - DepositAsset { assets: Wild(All), dest: beneficiary }, + DepositAsset { assets: Wild(All), beneficiary }, ], }], }; @@ -222,7 +222,7 @@ pub mod pallet { halt_on_error: false, xcm: vec![], }, - DepositAsset { assets: Wild(All), dest: beneficiary }, + DepositAsset { assets: Wild(All), beneficiary }, ], }; let weight = @@ -269,7 +269,7 @@ pub mod pallet { message: Xcm<()>, ) -> Result<(), XcmError> { let message = match interior { - MultiLocation::Null => message, + MultiLocation::Here => message, who => Xcm::<()>::RelayedFrom { who, message: Box::new(message) }, }; log::trace!(target: "xcm::send_xcm", "dest: {:?}, message: {:?}", &dest, &message); @@ -346,7 +346,7 @@ where #[cfg(feature = "runtime-benchmarks")] fn successful_origin() -> O { - O::from(Origin::Xcm(MultiLocation::Null)) + O::from(Origin::Xcm(MultiLocation::Here)) } } diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index 0fa4ba225ce2..b3c12f9531e3 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -133,9 +133,9 @@ impl pallet_balances::Config for Test { } parameter_types! { - pub const RelayLocation: MultiLocation = MultiLocation::Null; + pub const RelayLocation: MultiLocation = MultiLocation::Here; pub const AnyNetwork: NetworkId = NetworkId::Any; - pub Ancestry: MultiLocation = MultiLocation::Null; + pub Ancestry: MultiLocation = MultiLocation::Here; pub UnitWeightCost: Weight = 1_000; } diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index 6c1a7c6203cc..0e8b086e8290 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -43,14 +43,14 @@ fn send_works() { assets: (X1(Parent), SEND_AMOUNT).into(), effects: vec![ buy_execution(weight), - DepositAsset { assets: Wild(All), dest: sender.clone() }, + DepositAsset { assets: Wild(All), beneficiary: sender.clone() }, ], }; assert_ok!(XcmPallet::send(Origin::signed(ALICE), RelayLocation::get(), message.clone())); assert_eq!( sent_xcm(), vec![( - MultiLocation::Null, + MultiLocation::Here, RelayedFrom { who: sender.clone(), message: Box::new(message.clone()) } )] ); @@ -77,7 +77,7 @@ fn send_fails_when_xcm_router_blocks() { assets: (Parent, SEND_AMOUNT).into(), effects: vec![ buy_execution(weight), - DepositAsset { assets: Wild(All), dest: sender.clone() }, + DepositAsset { assets: Wild(All), beneficiary: sender.clone() }, ], }; assert_noop!( @@ -115,7 +115,7 @@ fn teleport_assets_works() { Origin::signed(ALICE), RelayLocation::get(), X1(AccountId32 { network: Any, id: BOB.into() }), - (Null, SEND_AMOUNT).into(), + (Here, SEND_AMOUNT).into(), weight, )); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT); @@ -143,7 +143,7 @@ fn reserve_transfer_assets_works() { Origin::signed(ALICE), Parachain(PARA_ID).into(), dest.clone(), - (Null, SEND_AMOUNT).into(), + (Here, SEND_AMOUNT).into(), weight )); // Alice spent amount @@ -157,7 +157,7 @@ fn reserve_transfer_assets_works() { Parachain(PARA_ID).into(), Xcm::ReserveAssetDeposited { assets: (X1(Parent), SEND_AMOUNT).into(), - effects: vec![buy_execution(weight), DepositAsset { assets: Wild(All), dest },] + effects: vec![buy_execution(weight), DepositAsset { assets: Wild(All), beneficiary: dest },] } )] ); @@ -184,8 +184,8 @@ fn execute_withdraw_to_deposit_works() { assert_ok!(XcmPallet::execute( Origin::signed(ALICE), Box::new(Xcm::WithdrawAsset { - assets: (Null, SEND_AMOUNT).into(), - effects: vec![buy_execution(weight), DepositAsset { assets: Wild(All), dest }], + assets: (Here, SEND_AMOUNT).into(), + effects: vec![buy_execution(weight), DepositAsset { assets: Wild(All), beneficiary: dest }], }), weight )); diff --git a/xcm/src/v1/mod.rs b/xcm/src/v1/mod.rs index 375a8d8a3cfc..5f7bae368d85 100644 --- a/xcm/src/v1/mod.rs +++ b/xcm/src/v1/mod.rs @@ -143,19 +143,19 @@ pub enum Xcm { /// created on this system. /// /// Some orders are given (`effects`) which should be executed once the corresponding derivative assets have - /// been placed into `holding`. + /// been placed into the Holding Register. /// - /// - `assets`: The asset(s) that are minted into holding. - /// - `effects`: The order(s) to execute on the holding register. + /// - `assets`: The asset(s) that are minted into the Holding Register. + /// - `effects`: The order(s) to execute on the Holding Register. /// - /// Safety: `origin` must be trusted to have irrevocably destroyed the `assets` prior as a consequence of - /// sending this message. + /// Safety: `origin` must be trusted to have irrevocably destroyed the corresponding`assets` prior as a consequence + /// of sending this message. /// /// Kind: *Trusted Indication*. /// /// Errors: #[codec(index = 2)] - TeleportAsset { assets: MultiAssets, effects: Vec> }, + ReceiveTeleportedAsset { assets: MultiAssets, effects: Vec> }, /// Indication of the contents of the holding register corresponding to the `QueryHolding` order of `query_id`. /// @@ -175,10 +175,10 @@ pub enum Xcm { }, /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the - /// ownership of `dest` within this consensus system. + /// ownership of `beneficiary`. /// /// - `assets`: The asset(s) to be withdrawn. - /// - `dest`: The new owner for the assets. + /// - `beneficiary`: The new owner for the assets. /// /// Safety: No concerns. /// @@ -186,15 +186,16 @@ pub enum Xcm { /// /// Errors: #[codec(index = 4)] - TransferAsset { assets: MultiAssets, dest: MultiLocation }, + TransferAsset { assets: MultiAssets, beneficiary: MultiLocation }, /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the - /// ownership of `dest` within this consensus system. + /// ownership of `dest` within this consensus system (i.e. its sovereign account). /// /// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`. /// /// - `assets`: The asset(s) to be withdrawn. - /// - `dest`: The new owner for the assets. + /// - `dest`: The location whose sovereign account will own the assets and thus the effective beneficiary for the + /// assets and the notification target for the reserve asset deposit message. /// - `effects`: The orders that should be contained in the `ReserveAssetDeposited` which is sent onwards to /// `dest`. /// @@ -303,10 +304,10 @@ impl Xcm { assets, effects: effects.into_iter().map(Order::into).collect(), }, - TeleportAsset { assets, effects } => - TeleportAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, + ReceiveTeleportedAsset { assets, effects } => + ReceiveTeleportedAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, QueryResponse { query_id: u64, response } => QueryResponse { query_id: u64, response }, - TransferAsset { assets, dest } => TransferAsset { assets, dest }, + TransferAsset { assets, beneficiary } => TransferAsset { assets, beneficiary }, TransferReserveAsset { assets, dest, effects } => TransferReserveAsset { assets, dest, effects }, HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => diff --git a/xcm/src/v1/multilocation.rs b/xcm/src/v1/multilocation.rs index 20a9153b570a..e44d5f76a289 100644 --- a/xcm/src/v1/multilocation.rs +++ b/xcm/src/v1/multilocation.rs @@ -42,11 +42,11 @@ use parity_scale_codec::{self, Decode, Encode}; /// /// This specific `MultiLocation` implementation uses a Rust `enum` in order to make pattern matching easier. /// -/// The `MultiLocation` value of `Null` simply refers to the interpreting consensus system. +/// The `MultiLocation` value of `Here` simply refers to the interpreting consensus system. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)] pub enum MultiLocation { /// The interpreting consensus system. - Null, + Here, /// A relative path comprising 1 junction. X1(Junction), /// A relative path comprising 2 junctions. @@ -76,7 +76,7 @@ impl From for MultiLocation { impl From<()> for MultiLocation { fn from(_: ()) -> Self { - MultiLocation::Null + MultiLocation::Here } } impl From<(Junction,)> for MultiLocation { @@ -128,7 +128,7 @@ impl From<(Junction, Junction, Junction, Junction, Junction, Junction, Junction, impl From<[Junction; 0]> for MultiLocation { fn from(_: [Junction; 0]) -> Self { - MultiLocation::Null + MultiLocation::Here } } impl From<[Junction; 1]> for MultiLocation { @@ -219,7 +219,7 @@ impl MultiLocation { /// Returns first junction, or `None` if the location is empty. pub fn first(&self) -> Option<&Junction> { match &self { - MultiLocation::Null => None, + MultiLocation::Here => None, MultiLocation::X1(ref a) => Some(a), MultiLocation::X2(ref a, ..) => Some(a), MultiLocation::X3(ref a, ..) => Some(a), @@ -234,7 +234,7 @@ impl MultiLocation { /// Returns last junction, or `None` if the location is empty. pub fn last(&self) -> Option<&Junction> { match &self { - MultiLocation::Null => None, + MultiLocation::Here => None, MultiLocation::X1(ref a) => Some(a), MultiLocation::X2(.., ref a) => Some(a), MultiLocation::X3(.., ref a) => Some(a), @@ -250,8 +250,8 @@ impl MultiLocation { /// (second item in tuple) or `None` if it was empty. pub fn split_first(self) -> (MultiLocation, Option) { match self { - MultiLocation::Null => (MultiLocation::Null, None), - MultiLocation::X1(a) => (MultiLocation::Null, Some(a)), + MultiLocation::Here => (MultiLocation::Here, None), + MultiLocation::X1(a) => (MultiLocation::Here, Some(a)), MultiLocation::X2(a, b) => (MultiLocation::X1(b), Some(a)), MultiLocation::X3(a, b, c) => (MultiLocation::X2(b, c), Some(a)), MultiLocation::X4(a, b, c, d) => (MultiLocation::X3(b, c, d), Some(a)), @@ -268,8 +268,8 @@ impl MultiLocation { /// (second item in tuple) or `None` if it was empty. pub fn split_last(self) -> (MultiLocation, Option) { match self { - MultiLocation::Null => (MultiLocation::Null, None), - MultiLocation::X1(a) => (MultiLocation::Null, Some(a)), + MultiLocation::Here => (MultiLocation::Here, None), + MultiLocation::X1(a) => (MultiLocation::Here, Some(a)), MultiLocation::X2(a, b) => (MultiLocation::X1(a), Some(b)), MultiLocation::X3(a, b, c) => (MultiLocation::X2(a, b), Some(c)), MultiLocation::X4(a, b, c, d) => (MultiLocation::X3(a, b, c), Some(d)), @@ -284,7 +284,7 @@ impl MultiLocation { /// Removes the first element from `self`, returning it (or `None` if it was empty). pub fn take_first(&mut self) -> Option { - let mut d = MultiLocation::Null; + let mut d = MultiLocation::Here; mem::swap(&mut *self, &mut d); let (tail, head) = d.split_first(); *self = tail; @@ -293,7 +293,7 @@ impl MultiLocation { /// Removes the last element from `self`, returning it (or `None` if it was empty). pub fn take_last(&mut self) -> Option { - let mut d = MultiLocation::Null; + let mut d = MultiLocation::Here; mem::swap(&mut *self, &mut d); let (head, tail) = d.split_last(); *self = head; @@ -304,7 +304,7 @@ impl MultiLocation { /// `self` in case of overflow. pub fn pushed_with(self, new: Junction) -> result::Result { Ok(match self { - MultiLocation::Null => MultiLocation::X1(new), + MultiLocation::Here => MultiLocation::X1(new), MultiLocation::X1(a) => MultiLocation::X2(a, new), MultiLocation::X2(a, b) => MultiLocation::X3(a, b, new), MultiLocation::X3(a, b, c) => MultiLocation::X4(a, b, c, new), @@ -320,7 +320,7 @@ impl MultiLocation { /// `self` in case of overflow. pub fn pushed_front_with(self, new: Junction) -> result::Result { Ok(match self { - MultiLocation::Null => MultiLocation::X1(new), + MultiLocation::Here => MultiLocation::X1(new), MultiLocation::X1(a) => MultiLocation::X2(new, a), MultiLocation::X2(a, b) => MultiLocation::X3(new, a, b), MultiLocation::X3(a, b, c) => MultiLocation::X4(new, a, b, c), @@ -335,7 +335,7 @@ impl MultiLocation { /// Returns the number of junctions in `self`. pub fn len(&self) -> usize { match &self { - MultiLocation::Null => 0, + MultiLocation::Here => 0, MultiLocation::X1(..) => 1, MultiLocation::X2(..) => 2, MultiLocation::X3(..) => 3, @@ -480,7 +480,7 @@ impl MultiLocation { /// Mutates `self`, suffixing it with `new`. Returns `Err` in case of overflow. pub fn push(&mut self, new: Junction) -> result::Result<(), ()> { - let mut n = MultiLocation::Null; + let mut n = MultiLocation::Here; mem::swap(&mut *self, &mut n); match n.pushed_with(new) { Ok(result) => { @@ -496,7 +496,7 @@ impl MultiLocation { /// Mutates `self`, prefixing it with `new`. Returns `Err` in case of overflow. pub fn push_front(&mut self, new: Junction) -> result::Result<(), ()> { - let mut n = MultiLocation::Null; + let mut n = MultiLocation::Here; mem::swap(&mut *self, &mut n); match n.pushed_front_with(new) { Ok(result) => { @@ -564,7 +564,7 @@ impl MultiLocation { /// This function ensures a multi-junction is in its canonicalized/normalized form, removing /// any internal `[Non-Parent, Parent]` combinations. pub fn canonicalize(&mut self) { - let mut normalized = MultiLocation::Null; + let mut normalized = MultiLocation::Here; let mut iter = self.iter(); // We build up the the new normalized path by taking items from the original multi-location. // When the next item we would add is `Parent`, we instead remove the last item assuming @@ -787,7 +787,7 @@ mod tests { let mut m = X6(Parachain(1), Parent, Parachain(2), Parent, Parachain(3), Parent); m.canonicalize(); - assert_eq!(m, Null); + assert_eq!(m, Here); let mut m = X5(Parachain(1), Parent, Parent, Parent, Parachain(3)); m.canonicalize(); @@ -795,7 +795,7 @@ mod tests { let mut m = X4(Parachain(1), Parachain(2), Parent, Parent); m.canonicalize(); - assert_eq!(m, Null); + assert_eq!(m, Here); let mut m = X4(Parent, Parent, Parachain(1), Parachain(2)); m.canonicalize(); diff --git a/xcm/src/v1/order.rs b/xcm/src/v1/order.rs index aed5a6ff3634..61d3530d8c45 100644 --- a/xcm/src/v1/order.rs +++ b/xcm/src/v1/order.rs @@ -40,15 +40,16 @@ pub enum Order { /// Errors: #[codec(index = 1)] // TODO: https://github.com/paritytech/polkadot/issues/3547 introduce `, max_assets: u32` - DepositAsset { assets: MultiAssetFilter, dest: MultiLocation }, + DepositAsset { assets: MultiAssetFilter, beneficiary: MultiLocation }, /// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within - /// this consensus system. + /// this consensus system (i.e. its sovereign account). /// /// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`. /// /// - `assets`: The asset(s) to remove from holding. - /// - `dest`: The new owner for the assets. + /// - `dest`: The location whose sovereign account will own the assets and thus the effective beneficiary for the + /// assets and the notification target for the reserve asset deposit message. /// - `effects`: The orders that should be contained in the `ReserveAssetDeposited` which is sent onwards to /// `dest`. /// @@ -84,12 +85,16 @@ pub enum Order { effects: Vec>, }, - /// Remove the asset(s) (`assets`) from holding and send a `TeleportAsset` XCM message to a destination location. + /// Remove the asset(s) (`assets`) from holding and send a `ReceiveTeleportedAsset` XCM message to a `destination` + /// location. /// /// - `assets`: The asset(s) to remove from holding. /// - `destination`: A valid location that has a bi-lateral teleportation arrangement. /// - `effects`: The orders to execute on the assets once arrived *on the destination location*. /// + /// NOTE: The `destination` location *MUST* respect this origin as a valid teleportation origin for all `assets`. + /// If it does not, then the assets may be lost. + /// /// Errors: #[codec(index = 5)] InitiateTeleport { assets: MultiAssetFilter, dest: MultiLocation, effects: Vec> }, @@ -139,7 +144,7 @@ impl Order { use Order::*; match order { Noop => Noop, - DepositAsset { assets, dest } => DepositAsset { assets, dest }, + DepositAsset { assets, beneficiary } => DepositAsset { assets, beneficiary }, DepositReserveAsset { assets, dest, effects } => DepositReserveAsset { assets, dest, effects }, ExchangeAsset { give, receive } => ExchangeAsset { give, receive }, diff --git a/xcm/xcm-builder/src/barriers.rs b/xcm/xcm-builder/src/barriers.rs index f253be18026f..ec1b5d0c7f63 100644 --- a/xcm/xcm-builder/src/barriers.rs +++ b/xcm/xcm-builder/src/barriers.rs @@ -51,7 +51,7 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFro ensure!(T::contains(origin), ()); ensure!(top_level, ()); match message { - Xcm::TeleportAsset { effects, .. } | + Xcm::ReceiveTeleportedAsset { effects, .. } | Xcm::WithdrawAsset { effects, .. } | Xcm::ReserveAssetDeposited { effects, .. } if matches!( diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index c8d6036577e7..af568a5fc8de 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -152,7 +152,7 @@ pub fn to_account(l: MultiLocation) -> Result { // Children at 1000+id X1(Parachain(id)) => 1000 + id as u64, // Self at 3000 - Null => 3000, + Here => 3000, // Parent at 3001 X1(Parent) => 3001, l => return Err(l), @@ -251,7 +251,7 @@ parameter_types! { pub static AllowUnpaidFrom: Vec = vec![]; pub static AllowPaidFrom: Vec = vec![]; // 1_000_000_000_000 => 1 unit of asset for 1 unit of Weight. - pub static WeightPrice: (AssetId, u128) = (Null.into(), 1_000_000_000_000); + pub static WeightPrice: (AssetId, u128) = (Here.into(), 1_000_000_000_000); } pub type TestBarrier = ( diff --git a/xcm/xcm-builder/src/origin_conversion.rs b/xcm/xcm-builder/src/origin_conversion.rs index 2cc343694fa9..1432738263cd 100644 --- a/xcm/xcm-builder/src/origin_conversion.rs +++ b/xcm/xcm-builder/src/origin_conversion.rs @@ -170,7 +170,7 @@ where // We institute a root fallback so root can always represent the context. This // guarantees that `successful_origin` will work. if o.caller() == Origin::root().caller() { - Ok(MultiLocation::Null) + Ok(MultiLocation::Here) } else { Err(o) } diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index 93c87ce5dad5..3c66ff705eac 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -32,7 +32,7 @@ fn basic_setup_works() { assert_eq!(to_account(X2(Parent, Parachain(50))), Ok(2050)); assert_eq!(to_account(X1(AccountIndex64 { index: 1, network: Any })), Ok(1)); assert_eq!(to_account(X1(AccountIndex64 { index: 42, network: Any })), Ok(42)); - assert_eq!(to_account(Null), Ok(3000)); + assert_eq!(to_account(Here), Ok(3000)); } #[test] @@ -47,7 +47,7 @@ fn weigher_should_work() { halt_on_error: true, xcm: vec![], }, - Order::DepositAsset { assets: All.into(), dest: Null }, + Order::DepositAsset { assets: All.into(), beneficiary: Here }, ], } .into(); @@ -56,7 +56,7 @@ fn weigher_should_work() { #[test] fn take_weight_credit_barrier_should_work() { - let mut message = opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), dest: Null }; + let mut message = opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), beneficiary: Here }; let mut weight_credit = 10; let r = @@ -72,7 +72,7 @@ fn take_weight_credit_barrier_should_work() { #[test] fn allow_unpaid_should_work() { - let mut message = opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), dest: Null }; + let mut message = opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), beneficiary: Here }; AllowUnpaidFrom::set(vec![X1(Parent)]); @@ -99,7 +99,7 @@ fn allow_unpaid_should_work() { fn allow_paid_should_work() { AllowPaidFrom::set(vec![X1(Parent)]); - let mut message = opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), dest: Null }; + let mut message = opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), beneficiary: Here }; let r = AllowTopLevelPaidExecutionFrom::>::should_execute( &X1(Parachain(1)), @@ -115,7 +115,7 @@ fn allow_paid_should_work() { assets: (X1(Parent), 100).into(), effects: vec![ Order::BuyExecution { fees, weight: 0, debt: 20, halt_on_error: true, xcm: vec![] }, - Order::DepositAsset { assets: All.into(), dest: Null }, + Order::DepositAsset { assets: All.into(), beneficiary: Here }, ], }; @@ -133,7 +133,7 @@ fn allow_paid_should_work() { assets: (X1(Parent), 100).into(), effects: vec![ Order::BuyExecution { fees, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, - Order::DepositAsset { assets: All.into(), dest: Null }, + Order::DepositAsset { assets: All.into(), beneficiary: Here }, ], }; @@ -159,8 +159,8 @@ fn allow_paid_should_work() { #[test] fn paying_reserve_deposit_should_work() { AllowPaidFrom::set(vec![X1(Parent)]); - add_reserve(X1(Parent), (X1(Parent), WildFungible).into()); - WeightPrice::set((X1(Parent).into(), 1_000_000_000_000)); + add_reserve(X1(Parent), (Parent, WildFungible).into()); + WeightPrice::set((Parent.into(), 1_000_000_000_000)); let origin = X1(Parent); let fees = (X1(Parent), 30).into(); @@ -174,7 +174,7 @@ fn paying_reserve_deposit_should_work() { halt_on_error: true, xcm: vec![], }, - Order::::DepositAsset { assets: All.into(), dest: Null }, + Order::::DepositAsset { assets: All.into(), beneficiary: Here }, ], }; let weight_limit = 50; @@ -188,19 +188,19 @@ fn transfer_should_work() { // we'll let them have message execution for free. AllowUnpaidFrom::set(vec![X1(Parachain(1))]); // Child parachain #1 owns 1000 tokens held by us in reserve. - add_asset(1001, (Null, 1000).into()); + add_asset(1001, (Here, 1000).into()); // They want to transfer 100 of them to their sibling parachain #2 let r = XcmExecutor::::execute_xcm( X1(Parachain(1)), Xcm::TransferAsset { - assets: (Null, 100).into(), - dest: X1(AccountIndex64 { index: 3, network: Any }), + assets: (Here, 100).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: Any }), }, 50, ); assert_eq!(r, Outcome::Complete(10)); - assert_eq!(assets(3), vec![(Null, 100).into()]); - assert_eq!(assets(1001), vec![(Null, 900).into()]); + assert_eq!(assets(3), vec![(Here, 100).into()]); + assert_eq!(assets(1001), vec![(Here, 900).into()]); assert_eq!(sent_xcm(), vec![]); } @@ -208,7 +208,7 @@ fn transfer_should_work() { fn reserve_transfer_should_work() { AllowUnpaidFrom::set(vec![X1(Parachain(1))]); // Child parachain #1 owns 1000 tokens held by us in reserve. - add_asset(1001, (Null, 1000).into()); + add_asset(1001, (Here, 1000).into()); // The remote account owned by gav. let three = X1(AccountIndex64 { index: 3, network: Any }); @@ -217,22 +217,22 @@ fn reserve_transfer_should_work() { let r = XcmExecutor::::execute_xcm( X1(Parachain(1)), Xcm::TransferReserveAsset { - assets: (Null, 100).into(), + assets: (Here, 100).into(), dest: X1(Parachain(2)), - effects: vec![Order::DepositAsset { assets: All.into(), dest: three.clone() }], + effects: vec![Order::DepositAsset { assets: All.into(), beneficiary: three.clone() }], }, 50, ); assert_eq!(r, Outcome::Complete(10)); - assert_eq!(assets(1002), vec![(Null, 100).into()]); + assert_eq!(assets(1002), vec![(Here, 100).into()]); assert_eq!( sent_xcm(), vec![( X1(Parachain(2)), Xcm::ReserveAssetDeposited { assets: (X1(Parent), 100).into(), - effects: vec![Order::DepositAsset { assets: All.into(), dest: three }], + effects: vec![Order::DepositAsset { assets: All.into(), beneficiary: three }], } )] ); @@ -287,8 +287,8 @@ fn transacting_should_refund_weight() { fn paid_transacting_should_refund_payment_for_unused_weight() { let one = X1(AccountIndex64 { index: 1, network: Any }); AllowPaidFrom::set(vec![one.clone()]); - add_asset(1, (X1(Parent), 100).into()); - WeightPrice::set((X1(Parent).into(), 1_000_000_000_000)); + add_asset(1, (Parent, 100).into()); + WeightPrice::set((Parent.into(), 1_000_000_000_000)); let origin = one.clone(); let fees = (X1(Parent), 100).into(); @@ -307,7 +307,7 @@ fn paid_transacting_should_refund_payment_for_unused_weight() { call: TestCall::Any(60, Some(10)).encode().into(), }], }, - Order::::DepositAsset { assets: All.into(), dest: one.clone() }, + Order::::DepositAsset { assets: All.into(), beneficiary: one.clone() }, ], }; let weight_limit = 100; diff --git a/xcm/xcm-builder/src/weight.rs b/xcm/xcm-builder/src/weight.rs index 028ce6b061e4..5e197fee89fa 100644 --- a/xcm/xcm-builder/src/weight.rs +++ b/xcm/xcm-builder/src/weight.rs @@ -37,7 +37,7 @@ impl, C: Decode + GetDispatchInfo> WeightBounds for FixedWeigh T::get().saturating_add(Self::shallow(message.as_mut())?), Xcm::WithdrawAsset { effects, .. } | Xcm::ReserveAssetDeposited { effects, .. } | - Xcm::TeleportAsset { effects, .. } => { + Xcm::ReceiveTeleportedAsset { effects, .. } => { let inner: Weight = effects .iter_mut() .map(|effect| match effect { @@ -62,7 +62,7 @@ impl, C: Decode + GetDispatchInfo> WeightBounds for FixedWeigh Xcm::RelayedFrom { ref mut message, .. } => Self::deep(message.as_mut())?, Xcm::WithdrawAsset { effects, .. } | Xcm::ReserveAssetDeposited { effects, .. } | - Xcm::TeleportAsset { effects, .. } => { + Xcm::ReceiveTeleportedAsset { effects, .. } => { let mut extra = 0; for effect in effects.iter_mut() { match effect { diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 5270a30d44d2..8ce2ee4e2de2 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -339,12 +339,12 @@ impl Assets { /// ``` /// use xcm_executor::Assets; /// use xcm::v1::prelude::*; - /// let assets_i_have: Assets = vec![ (Null, 100).into(), (vec![0], 100).into() ].into(); - /// let assets_they_want: MultiAssetFilter = vec![ (Null, 200).into(), (vec![0], 50).into() [.into(); + /// let assets_i_have: Assets = vec![ (Here, 100).into(), (vec![0], 100).into() ].into(); + /// let assets_they_want: MultiAssetFilter = vec![ (Here, 200).into(), (vec![0], 50).into() [.into(); /// /// let assets_we_can_trade: Assets = assets_i_have.min(&assets_they_want); /// assert_eq!(assets_we_can_trade.into_assets_iter().collect::>(), vec![ - /// (Null, 100).into(), (vec![0], 50).into(), + /// (Here, 100).into(), (vec![0], 50).into(), /// ]); /// ``` pub fn min(&self, mask: &MultiAssetFilter) -> Assets { @@ -388,7 +388,7 @@ impl Assets { mod tests { use super::*; use xcm::v1::prelude::*; - use MultiLocation::Null; + use MultiLocation::Here; #[allow(non_snake_case)] fn AF(id: u8, amount: u128) -> MultiAsset { (vec![id], amount).into() @@ -399,11 +399,11 @@ mod tests { } #[allow(non_snake_case)] fn CF(amount: u128) -> MultiAsset { - (Null, amount).into() + (Here, amount).into() } #[allow(non_snake_case)] fn CNF(instance_id: u128) -> MultiAsset { - (Null, AssetInstance::Index { id: instance_id }).into() + (Here, AssetInstance::Index { id: instance_id }).into() } fn test_assets() -> Assets { @@ -517,8 +517,8 @@ mod tests { #[test] fn min_all_concrete_works() { let assets = test_assets(); - let fungible = Wild((Null, WildFungible).into()); - let non_fungible = Wild((Null, WildNonFungible).into()); + let fungible = Wild((Here, WildFungible).into()); + let non_fungible = Wild((Here, WildNonFungible).into()); let fungible = assets.min(&fungible); let fungible = fungible.assets_iter().collect::>(); @@ -581,8 +581,8 @@ mod tests { #[test] fn saturating_take_all_concrete_works() { let mut assets = test_assets(); - let fungible = Wild((Null, WildFungible).into()); - let non_fungible = Wild((Null, WildNonFungible).into()); + let fungible = Wild((Here, WildFungible).into()); + let non_fungible = Wild((Here, WildNonFungible).into()); let fungible = assets.saturating_take(fungible); let fungible = fungible.assets_iter().collect::>(); diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 491558dbdb28..ed7f0b8e5053 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -162,10 +162,10 @@ impl XcmExecutor { } Some((assets.into(), effects)) }, - (origin, Xcm::TransferAsset { assets, dest }) => { + (origin, Xcm::TransferAsset { assets, beneficiary }) => { // Take `assets` from the origin account (on-chain) and place into dest account. for asset in assets.inner() { - Config::AssetTransactor::beam_asset(&asset, &origin, &dest)?; + Config::AssetTransactor::beam_asset(&asset, &origin, &beneficiary)?; } None }, @@ -179,7 +179,7 @@ impl XcmExecutor { Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposited { assets, effects })?; None }, - (origin, Xcm::TeleportAsset { assets, effects }) => { + (origin, Xcm::ReceiveTeleportedAsset { assets, effects }) => { // check whether we trust origin to teleport this asset to us via config trait. for asset in assets.inner() { // We only trust the origin to send us assets that they identify as their @@ -269,10 +269,10 @@ impl XcmExecutor { ); let mut total_surplus = 0; match effect { - Order::DepositAsset { assets, dest } => { + Order::DepositAsset { assets, beneficiary } => { let deposited = holding.saturating_take(assets); for asset in deposited.into_assets_iter() { - Config::AssetTransactor::deposit_asset(&asset, &dest)?; + Config::AssetTransactor::deposit_asset(&asset, &beneficiary)?; } }, Order::DepositReserveAsset { assets, dest, effects } => { @@ -294,7 +294,7 @@ impl XcmExecutor { Config::AssetTransactor::check_out(&origin, &asset); } let assets = Self::reanchored(assets, &dest); - Config::XcmSender::send_xcm(dest, Xcm::TeleportAsset { assets, effects })?; + Config::XcmSender::send_xcm(dest, Xcm::ReceiveTeleportedAsset { assets, effects })?; }, Order::QueryHolding { query_id, dest, assets } => { let assets = Self::reanchored(holding.min(&assets), &dest); diff --git a/xcm/xcm-executor/src/traits/transact_asset.rs b/xcm/xcm-executor/src/traits/transact_asset.rs index 60fb0508c676..a810ce173ca3 100644 --- a/xcm/xcm-executor/src/traits/transact_asset.rs +++ b/xcm/xcm-executor/src/traits/transact_asset.rs @@ -193,7 +193,7 @@ impl TransactAsset for Tuple { #[cfg(test)] mod tests { use super::*; - use MultiLocation::Null; + use MultiLocation::Here; pub struct UnimplementedTransactor; impl TransactAsset for UnimplementedTransactor {} @@ -273,7 +273,7 @@ mod tests { (UnimplementedTransactor, NotFoundTransactor, UnimplementedTransactor); assert_eq!( - MultiTransactor::deposit_asset(&(Null, 1).into(), &Null), + MultiTransactor::deposit_asset(&(Here, 1).into(), &Here), Err(XcmError::AssetNotFound) ); } @@ -282,7 +282,7 @@ mod tests { fn unimplemented_and_not_found_continue_iteration() { type MultiTransactor = (UnimplementedTransactor, NotFoundTransactor, SuccessfulTransactor); - assert_eq!(MultiTransactor::deposit_asset(&(Null, 1).into(), &Null), Ok(())); + assert_eq!(MultiTransactor::deposit_asset(&(Here, 1).into(), &Here), Ok(())); } #[test] @@ -290,7 +290,7 @@ mod tests { type MultiTransactor = (OverflowTransactor, SuccessfulTransactor); assert_eq!( - MultiTransactor::deposit_asset(&(Null, 1).into(), &Null), + MultiTransactor::deposit_asset(&(Here, 1).into(), &Here), Err(XcmError::Overflow) ); } @@ -299,6 +299,6 @@ mod tests { fn success_stops_iteration() { type MultiTransactor = (SuccessfulTransactor, OverflowTransactor); - assert_eq!(MultiTransactor::deposit_asset(&(Null, 1).into(), &Null), Ok(())); + assert_eq!(MultiTransactor::deposit_asset(&(Here, 1).into(), &Here), Ok(())); } } diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 978795a1546e..305865508bf5 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -112,7 +112,7 @@ mod tests { ); Relay::execute_with(|| { assert_ok!(RelayChainPalletXcm::send_xcm( - Null, + Here, X1(Parachain(1)), Transact { origin_type: OriginKind::SovereignAccount, @@ -139,7 +139,7 @@ mod tests { ); ParaA::execute_with(|| { assert_ok!(ParachainPalletXcm::send_xcm( - Null, + Here, X1(Parent), Transact { origin_type: OriginKind::SovereignAccount, @@ -166,7 +166,7 @@ mod tests { ); ParaA::execute_with(|| { assert_ok!(ParachainPalletXcm::send_xcm( - Null, + Here, X2(Parent, Parachain(2)), Transact { origin_type: OriginKind::SovereignAccount, @@ -193,7 +193,7 @@ mod tests { relay_chain::Origin::signed(ALICE), X1(Parachain(1)), X1(Junction::AccountId32 { network: NetworkId::Any, id: ALICE.into() }), - (Null, 123).into(), + (Here, 123).into(), 123, )); }); diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index 7472df198219..e691e3e7ad35 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -91,10 +91,10 @@ impl shared::Config for Runtime {} impl configuration::Config for Runtime {} parameter_types! { - pub const KsmLocation: MultiLocation = MultiLocation::Null; + pub const KsmLocation: MultiLocation = MultiLocation::Here; pub const KusamaNetwork: NetworkId = NetworkId::Kusama; pub const AnyNetwork: NetworkId = NetworkId::Any; - pub Ancestry: MultiLocation = MultiLocation::Null; + pub Ancestry: MultiLocation = MultiLocation::Here; pub UnitWeightCost: Weight = 1_000; } From e9dbc72d84f15711d3b03d7daedb77559a2f8d31 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 12:32:43 +0200 Subject: [PATCH 68/84] Introduce --- xcm/src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/xcm/src/lib.rs b/xcm/src/lib.rs index f9ce3a3f5266..e7d31a2a7d74 100644 --- a/xcm/src/lib.rs +++ b/xcm/src/lib.rs @@ -29,6 +29,10 @@ use parity_scale_codec::{Decode, Encode, Error as CodecError, Input}; pub mod v1; +pub mod latest { + pub use super::v1::*; +} + mod double_encoded; pub use double_encoded::DoubleEncoded; @@ -75,6 +79,10 @@ pub mod opaque { pub use crate::v1::opaque::{Order, Xcm}; } + pub mod latest { + pub use super::v1::*; + } + /// The basic `VersionedXcm` type which just uses the `Vec` as an encoded call. pub type VersionedXcm = super::VersionedXcm<()>; } From 36ccb47bdc1cb1e20e45c2c6dcee4b9dac325624 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 13:00:12 +0200 Subject: [PATCH 69/84] More ergonomics --- xcm/src/v1/multiasset.rs | 53 +++++++++++++++++++++---- xcm/xcm-executor/src/assets.rs | 70 +++++++++++++++++----------------- 2 files changed, 81 insertions(+), 42 deletions(-) diff --git a/xcm/src/v1/multiasset.rs b/xcm/src/v1/multiasset.rs index 5716a7b5c980..cc0672b2d2d8 100644 --- a/xcm/src/v1/multiasset.rs +++ b/xcm/src/v1/multiasset.rs @@ -39,10 +39,7 @@ pub enum AssetInstance { /// A compact index. Technically this could be greater than `u128`, but this implementation supports only /// values up to `2**128 - 1`. - Index { - #[codec(compact)] - id: u128, - }, + Index(#[codec(compact)] u128), /// A 4-byte fixed-length datum. Array4([u8; 4]), @@ -60,6 +57,48 @@ pub enum AssetInstance { Blob(Vec), } +impl From<()> for AssetInstance { + fn from(_: ()) -> Self { + Self::Undefined + } +} + +/*impl From for AssetInstance { + fn from(x: u128) -> Self { + Self::Index(x) + } +}*/ + +impl From<[u8; 4]> for AssetInstance { + fn from(x: [u8; 4]) -> Self { + Self::Array4(x) + } +} + +impl From<[u8; 8]> for AssetInstance { + fn from(x: [u8; 8]) -> Self { + Self::Array8(x) + } +} + +impl From<[u8; 16]> for AssetInstance { + fn from(x: [u8; 16]) -> Self { + Self::Array16(x) + } +} + +impl From<[u8; 32]> for AssetInstance { + fn from(x: [u8; 32]) -> Self { + Self::Array32(x) + } +} + +impl From> for AssetInstance { + fn from(x: Vec) -> Self { + Self::Blob(x) + } +} + /// Classification of an asset being concrete or abstract. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)] pub enum AssetId { @@ -128,9 +167,9 @@ impl From for Fungibility { } } -impl From for Fungibility { - fn from(instance: AssetInstance) -> Fungibility { - Fungibility::NonFungible(instance) +impl> From for Fungibility { + fn from(instance: T) -> Fungibility { + Fungibility::NonFungible(instance.into()) } } diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 8ce2ee4e2de2..fb9394d22d1d 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -394,24 +394,24 @@ mod tests { (vec![id], amount).into() } #[allow(non_snake_case)] - fn ANF(class: u8, instance_id: u128) -> MultiAsset { - (vec![class], AssetInstance::Index { id: instance_id }).into() + fn ANF(class: u8, instance_id: u8) -> MultiAsset { + (vec![class], vec![instance_id]).into() } #[allow(non_snake_case)] fn CF(amount: u128) -> MultiAsset { (Here, amount).into() } #[allow(non_snake_case)] - fn CNF(instance_id: u128) -> MultiAsset { - (Here, AssetInstance::Index { id: instance_id }).into() + fn CNF(instance_id: u8) -> MultiAsset { + (Here, [instance_id; 4]).into() } fn test_assets() -> Assets { let mut assets = Assets::new(); assets.subsume(AF(1, 100)); - assets.subsume(ANF(2, 200)); + assets.subsume(ANF(2, 20)); assets.subsume(CF(300)); - assets.subsume(CNF(400)); + assets.subsume(CNF(40)); assets } @@ -420,9 +420,9 @@ mod tests { let t1 = test_assets(); let mut t2 = Assets::new(); t2.subsume(AF(1, 50)); - t2.subsume(ANF(2, 100)); + t2.subsume(ANF(2, 10)); t2.subsume(CF(300)); - t2.subsume(CNF(500)); + t2.subsume(CNF(50)); let mut r1 = t1.clone(); r1.subsume_assets(t2.clone()); let mut r2 = t1.clone(); @@ -443,12 +443,12 @@ mod tests { let t = t.checked_sub(CF(151)).unwrap_err(); let t = t.checked_sub(CF(150)).unwrap(); let t = t.checked_sub(CF(1)).unwrap_err(); - let t = t.checked_sub(ANF(2, 201)).unwrap_err(); - let t = t.checked_sub(ANF(2, 200)).unwrap(); - let t = t.checked_sub(ANF(2, 200)).unwrap_err(); - let t = t.checked_sub(CNF(401)).unwrap_err(); - let t = t.checked_sub(CNF(400)).unwrap(); - let t = t.checked_sub(CNF(400)).unwrap_err(); + let t = t.checked_sub(ANF(2, 21)).unwrap_err(); + let t = t.checked_sub(ANF(2, 20)).unwrap(); + let t = t.checked_sub(ANF(2, 20)).unwrap_err(); + let t = t.checked_sub(CNF(41)).unwrap_err(); + let t = t.checked_sub(CNF(40)).unwrap(); + let t = t.checked_sub(CNF(40)).unwrap_err(); assert_eq!(t, Assets::new()); } @@ -459,8 +459,8 @@ mod tests { // Order defined by implementation: CF, AF, CNF, ANF assert_eq!(Some(CF(300)), iter.next()); assert_eq!(Some(AF(1, 100)), iter.next()); - assert_eq!(Some(CNF(400)), iter.next()); - assert_eq!(Some(ANF(2, 200)), iter.next()); + assert_eq!(Some(CNF(40)), iter.next()); + assert_eq!(Some(ANF(2, 20)), iter.next()); assert_eq!(None, iter.next()); } @@ -468,14 +468,14 @@ mod tests { fn assets_into_works() { let mut assets_vec: Vec = Vec::new(); assets_vec.push(AF(1, 100)); - assets_vec.push(ANF(2, 200)); + assets_vec.push(ANF(2, 20)); assets_vec.push(CF(300)); - assets_vec.push(CNF(400)); + assets_vec.push(CNF(40)); // Push same group of tokens again assets_vec.push(AF(1, 100)); - assets_vec.push(ANF(2, 200)); + assets_vec.push(ANF(2, 20)); assets_vec.push(CF(300)); - assets_vec.push(CNF(400)); + assets_vec.push(CNF(40)); let assets: Assets = assets_vec.into(); let mut iter = assets.into_assets_iter(); @@ -483,8 +483,8 @@ mod tests { assert_eq!(Some(CF(600)), iter.next()); assert_eq!(Some(AF(1, 200)), iter.next()); // Non-fungibles collapse - assert_eq!(Some(CNF(400)), iter.next()); - assert_eq!(Some(ANF(2, 200)), iter.next()); + assert_eq!(Some(CNF(40)), iter.next()); + assert_eq!(Some(ANF(2, 20)), iter.next()); assert_eq!(None, iter.next()); } @@ -511,7 +511,7 @@ mod tests { assert_eq!(fungible, vec![AF(1, 100)]); let non_fungible = assets.min(&non_fungible); let non_fungible = non_fungible.assets_iter().collect::>(); - assert_eq!(non_fungible, vec![ANF(2, 200)]); + assert_eq!(non_fungible, vec![ANF(2, 20)]); } #[test] @@ -525,7 +525,7 @@ mod tests { assert_eq!(fungible, vec![CF(300)]); let non_fungible = assets.min(&non_fungible); let non_fungible = non_fungible.assets_iter().collect::>(); - assert_eq!(non_fungible, vec![CNF(400)]); + assert_eq!(non_fungible, vec![CNF(40)]); } #[test] @@ -536,16 +536,16 @@ mod tests { // This is less than 100, so it will decrease to 50 assets2.subsume(AF(1, 50)); // This asset does not exist, so not included - assets2.subsume(ANF(2, 400)); + assets2.subsume(ANF(2, 40)); // This is more then 300, so it should stay at 300 assets2.subsume(CF(600)); // This asset should be included - assets2.subsume(CNF(400)); + assets2.subsume(CNF(40)); let assets2: MultiAssets = assets2.into(); let assets_min = assets1.min(&assets2.into()); let assets_min = assets_min.into_assets_iter().collect::>(); - assert_eq!(assets_min, vec![CF(300), AF(1, 50), CNF(400)]); + assert_eq!(assets_min, vec![CF(300), AF(1, 50), CNF(40)]); } #[test] @@ -572,10 +572,10 @@ mod tests { assert_eq!(fungible, vec![AF(1, 100)]); let non_fungible = assets.saturating_take(non_fungible); let non_fungible = non_fungible.assets_iter().collect::>(); - assert_eq!(non_fungible, vec![ANF(2, 200)]); + assert_eq!(non_fungible, vec![ANF(2, 20)]); // Assets drained of abstract let final_assets = assets.assets_iter().collect::>(); - assert_eq!(final_assets, vec![CF(300), CNF(400)]); + assert_eq!(final_assets, vec![CF(300), CNF(40)]); } #[test] @@ -589,10 +589,10 @@ mod tests { assert_eq!(fungible, vec![CF(300)]); let non_fungible = assets.saturating_take(non_fungible); let non_fungible = non_fungible.assets_iter().collect::>(); - assert_eq!(non_fungible, vec![CNF(400)]); + assert_eq!(non_fungible, vec![CNF(40)]); // Assets drained of concrete let assets = assets.assets_iter().collect::>(); - assert_eq!(assets, vec![AF(1, 100), ANF(2, 200)]); + assert_eq!(assets, vec![AF(1, 100), ANF(2, 20)]); } #[test] @@ -603,18 +603,18 @@ mod tests { // We should take 50 assets2.subsume(AF(1, 50)); // This asset should not be taken - assets2.subsume(ANF(2, 400)); + assets2.subsume(ANF(2, 40)); // This is more then 300, so it takes everything assets2.subsume(CF(600)); // This asset should be taken - assets2.subsume(CNF(400)); + assets2.subsume(CNF(40)); let assets2: MultiAssets = assets2.into(); let taken = assets1.saturating_take(assets2.into()); let taken = taken.into_assets_iter().collect::>(); - assert_eq!(taken, vec![CF(300), AF(1, 50), CNF(400)]); + assert_eq!(taken, vec![CF(300), AF(1, 50), CNF(40)]); let assets = assets1.into_assets_iter().collect::>(); - assert_eq!(assets, vec![AF(1, 50), ANF(2, 200)]); + assert_eq!(assets, vec![AF(1, 50), ANF(2, 20)]); } } From de33d7d1cc392a16f47989a727520de3fbd32478 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 13:00:20 +0200 Subject: [PATCH 70/84] More ergonomics --- xcm/pallet-xcm/src/tests.rs | 10 ++++++++-- xcm/src/v1/mod.rs | 6 ++++-- xcm/xcm-builder/src/tests.rs | 9 ++++++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index 0e8b086e8290..9285817c5270 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -157,7 +157,10 @@ fn reserve_transfer_assets_works() { Parachain(PARA_ID).into(), Xcm::ReserveAssetDeposited { assets: (X1(Parent), SEND_AMOUNT).into(), - effects: vec![buy_execution(weight), DepositAsset { assets: Wild(All), beneficiary: dest },] + effects: vec![ + buy_execution(weight), + DepositAsset { assets: Wild(All), beneficiary: dest }, + ] } )] ); @@ -185,7 +188,10 @@ fn execute_withdraw_to_deposit_works() { Origin::signed(ALICE), Box::new(Xcm::WithdrawAsset { assets: (Here, SEND_AMOUNT).into(), - effects: vec![buy_execution(weight), DepositAsset { assets: Wild(All), beneficiary: dest }], + effects: vec![ + buy_execution(weight), + DepositAsset { assets: Wild(All), beneficiary: dest } + ], }), weight )); diff --git a/xcm/src/v1/mod.rs b/xcm/src/v1/mod.rs index 5f7bae368d85..c27bb1788069 100644 --- a/xcm/src/v1/mod.rs +++ b/xcm/src/v1/mod.rs @@ -304,8 +304,10 @@ impl Xcm { assets, effects: effects.into_iter().map(Order::into).collect(), }, - ReceiveTeleportedAsset { assets, effects } => - ReceiveTeleportedAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, + ReceiveTeleportedAsset { assets, effects } => ReceiveTeleportedAsset { + assets, + effects: effects.into_iter().map(Order::into).collect(), + }, QueryResponse { query_id: u64, response } => QueryResponse { query_id: u64, response }, TransferAsset { assets, beneficiary } => TransferAsset { assets, beneficiary }, TransferReserveAsset { assets, dest, effects } => diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index 3c66ff705eac..f918fb67124e 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -56,7 +56,8 @@ fn weigher_should_work() { #[test] fn take_weight_credit_barrier_should_work() { - let mut message = opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), beneficiary: Here }; + let mut message = + opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), beneficiary: Here }; let mut weight_credit = 10; let r = @@ -72,7 +73,8 @@ fn take_weight_credit_barrier_should_work() { #[test] fn allow_unpaid_should_work() { - let mut message = opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), beneficiary: Here }; + let mut message = + opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), beneficiary: Here }; AllowUnpaidFrom::set(vec![X1(Parent)]); @@ -99,7 +101,8 @@ fn allow_unpaid_should_work() { fn allow_paid_should_work() { AllowPaidFrom::set(vec![X1(Parent)]); - let mut message = opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), beneficiary: Here }; + let mut message = + opaque::Xcm::TransferAsset { assets: (X1(Parent), 100).into(), beneficiary: Here }; let r = AllowTopLevelPaidExecutionFrom::>::should_execute( &X1(Parachain(1)), From 7ff049359905c9bd54e018e97d01bebcd5459f11 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 14:42:05 +0200 Subject: [PATCH 71/84] test fix --- xcm/xcm-simulator/example/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 305865508bf5..12e39a51e30f 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -192,7 +192,7 @@ mod tests { assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( relay_chain::Origin::signed(ALICE), X1(Parachain(1)), - X1(Junction::AccountId32 { network: NetworkId::Any, id: ALICE.into() }), + X1(AccountId32 { network: Any, id: ALICE.into() }), (Here, 123).into(), 123, )); From 84c44f8244732e3fbf499a5ce54806aa42191ad2 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 14:48:47 +0200 Subject: [PATCH 72/84] test fixes --- xcm/pallet-xcm/src/tests.rs | 8 ++++---- xcm/xcm-builder/src/tests.rs | 14 +++++++------- xcm/xcm-executor/src/lib.rs | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index 9285817c5270..dd86ca3a05e0 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -43,7 +43,7 @@ fn send_works() { assets: (X1(Parent), SEND_AMOUNT).into(), effects: vec![ buy_execution(weight), - DepositAsset { assets: Wild(All), beneficiary: sender.clone() }, + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() }, ], }; assert_ok!(XcmPallet::send(Origin::signed(ALICE), RelayLocation::get(), message.clone())); @@ -77,7 +77,7 @@ fn send_fails_when_xcm_router_blocks() { assets: (Parent, SEND_AMOUNT).into(), effects: vec![ buy_execution(weight), - DepositAsset { assets: Wild(All), beneficiary: sender.clone() }, + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() }, ], }; assert_noop!( @@ -159,7 +159,7 @@ fn reserve_transfer_assets_works() { assets: (X1(Parent), SEND_AMOUNT).into(), effects: vec![ buy_execution(weight), - DepositAsset { assets: Wild(All), beneficiary: dest }, + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest }, ] } )] @@ -190,7 +190,7 @@ fn execute_withdraw_to_deposit_works() { assets: (Here, SEND_AMOUNT).into(), effects: vec![ buy_execution(weight), - DepositAsset { assets: Wild(All), beneficiary: dest } + DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest } ], }), weight diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index f918fb67124e..86ad5ccc7720 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -47,7 +47,7 @@ fn weigher_should_work() { halt_on_error: true, xcm: vec![], }, - Order::DepositAsset { assets: All.into(), beneficiary: Here }, + Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here }, ], } .into(); @@ -118,7 +118,7 @@ fn allow_paid_should_work() { assets: (X1(Parent), 100).into(), effects: vec![ Order::BuyExecution { fees, weight: 0, debt: 20, halt_on_error: true, xcm: vec![] }, - Order::DepositAsset { assets: All.into(), beneficiary: Here }, + Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here }, ], }; @@ -136,7 +136,7 @@ fn allow_paid_should_work() { assets: (X1(Parent), 100).into(), effects: vec![ Order::BuyExecution { fees, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, - Order::DepositAsset { assets: All.into(), beneficiary: Here }, + Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here }, ], }; @@ -177,7 +177,7 @@ fn paying_reserve_deposit_should_work() { halt_on_error: true, xcm: vec![], }, - Order::::DepositAsset { assets: All.into(), beneficiary: Here }, + Order::::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here }, ], }; let weight_limit = 50; @@ -222,7 +222,7 @@ fn reserve_transfer_should_work() { Xcm::TransferReserveAsset { assets: (Here, 100).into(), dest: X1(Parachain(2)), - effects: vec![Order::DepositAsset { assets: All.into(), beneficiary: three.clone() }], + effects: vec![Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: three.clone() }], }, 50, ); @@ -235,7 +235,7 @@ fn reserve_transfer_should_work() { X1(Parachain(2)), Xcm::ReserveAssetDeposited { assets: (X1(Parent), 100).into(), - effects: vec![Order::DepositAsset { assets: All.into(), beneficiary: three }], + effects: vec![Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: three }], } )] ); @@ -310,7 +310,7 @@ fn paid_transacting_should_refund_payment_for_unused_weight() { call: TestCall::Any(60, Some(10)).encode().into(), }], }, - Order::::DepositAsset { assets: All.into(), beneficiary: one.clone() }, + Order::::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: one.clone() }, ], }; let weight_limit = 100; diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index e01f8ebbd0a7..e59aa54a1fd8 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -270,7 +270,7 @@ impl XcmExecutor { let mut total_surplus = 0; match effect { Order::DepositAsset { assets, max_assets, beneficiary } => { - let deposited = holding.limited_saturating_take(assets, max_assets); + let deposited = holding.limited_saturating_take(assets, max_assets as usize); for asset in deposited.into_assets_iter() { Config::AssetTransactor::deposit_asset(&asset, &beneficiary)?; } From 7d52b93684791097df80d0a44f16801397a00659 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 14:59:56 +0200 Subject: [PATCH 73/84] docs --- xcm/src/v1/order.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/xcm/src/v1/order.rs b/xcm/src/v1/order.rs index f17b12357a7d..b9f1c6afd2b2 100644 --- a/xcm/src/v1/order.rs +++ b/xcm/src/v1/order.rs @@ -31,11 +31,14 @@ pub enum Order { #[codec(index = 0)] Noop, - /// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within - /// this consensus system. + /// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `beneficiary` + /// within this consensus system. /// /// - `assets`: The asset(s) to remove from holding. - /// - `dest`: The new owner for the assets. + /// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. Only the first + /// `max_assets` assets/instances of those matched by `assets` will be removed, prioritised under standard asset + /// ordering. Any others will remain in holding. + /// - `beneficiary`: The new owner for the assets. /// /// Errors: #[codec(index = 1)] @@ -47,6 +50,9 @@ pub enum Order { /// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`. /// /// - `assets`: The asset(s) to remove from holding. + /// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. Only the first + /// `max_assets` assets/instances of those matched by `assets` will be removed, prioritised under standard asset + /// ordering. Any others will remain in holding. /// - `dest`: The location whose sovereign account will own the assets and thus the effective beneficiary for the /// assets and the notification target for the reserve asset deposit message. /// - `effects`: The orders that should be contained in the `ReserveAssetDeposited` which is sent onwards to From 51c7d0554c4822df1b9bc7122c92da5ba9f19fe9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 15:43:04 +0200 Subject: [PATCH 74/84] BuyExecution includes --- xcm/pallet-xcm/src/lib.rs | 6 ++- xcm/pallet-xcm/src/mock.rs | 3 +- xcm/src/v1/order.rs | 35 +++++++++++++----- xcm/xcm-builder/src/tests.rs | 37 ++++++++++++++----- xcm/xcm-builder/src/weight.rs | 67 +++++++++++++++++++++------------- xcm/xcm-executor/src/assets.rs | 41 +++++++++++---------- xcm/xcm-executor/src/lib.rs | 30 ++++++++++----- 7 files changed, 144 insertions(+), 75 deletions(-) diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index 676c3205bd65..bbbcfe1163b3 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -163,7 +163,8 @@ pub mod pallet { weight: 0, debt: dest_weight, halt_on_error: false, - xcm: vec![], + orders: vec![], + instructions: vec![], }, DepositAsset { assets: Wild(All), max_assets: 1, beneficiary }, ], @@ -220,7 +221,8 @@ pub mod pallet { weight: 0, debt: dest_weight, halt_on_error: false, - xcm: vec![], + orders: vec![], + instructions: vec![], }, DepositAsset { assets: Wild(All), max_assets: 1, beneficiary }, ], diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index b3c12f9531e3..50a2471d74d7 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -201,7 +201,8 @@ pub(crate) fn buy_execution(debt: Weight) -> Order { weight: 0, debt, halt_on_error: false, - xcm: vec![], + orders: vec![], + instructions: vec![], } } diff --git a/xcm/src/v1/order.rs b/xcm/src/v1/order.rs index b9f1c6afd2b2..6a2b04e9acf3 100644 --- a/xcm/src/v1/order.rs +++ b/xcm/src/v1/order.rs @@ -60,7 +60,12 @@ pub enum Order { /// /// Errors: #[codec(index = 2)] - DepositReserveAsset { assets: MultiAssetFilter, max_assets: u32, dest: MultiLocation, effects: Vec> }, + DepositReserveAsset { + assets: MultiAssetFilter, + max_assets: u32, + dest: MultiLocation, + effects: Vec>, + }, /// Remove the asset(s) (`give`) from holding and replace them with alternative assets. /// @@ -120,11 +125,20 @@ pub enum Order { assets: MultiAssetFilter, }, - /// Pay for the execution of some XCM with up to `weight` picoseconds of execution time, paying for this with - /// up to `fees` from the holding register. + /// Pay for the execution of some XCM `instructions` and `orders` with up to `weight` picoseconds of execution time, + /// paying for this with up to `fees` from the Holding Register. /// /// - `fees`: The asset(s) to remove from holding to pay for fees. - /// + /// - `weight`: The amount of weight to purchase; this should be at least the shallow weight of `effects` and `xcm`. + /// - `debt`: The amount of weight-debt already incurred to be pay off; this should be equal to the unpaid weight of + /// any surrounding operations/orders. + /// - `halt_on_error`: If `true`, the execution of the `orders` and `operations` will halt on the first failure. If + /// `false`, then execution will continue regardless. + /// - `orders`: Orders to be executed with the existing Holding Register; execution of these orders happens PRIOR to + /// execution of the `operations`. The (shallow) weight for these must be paid for with the `weight` purchased. + /// - `instructions`: XCM instructions to be executed outside of the context of the current Holding Register; + /// execution of these instructions happens AFTER the execution of the `orders`. The (shallow) weight for these + /// must be paid for with the `weight` purchased. /// Errors: #[codec(index = 7)] BuyExecution { @@ -132,7 +146,8 @@ pub enum Order { weight: u64, debt: u64, halt_on_error: bool, - xcm: Vec>, + orders: Vec>, + instructions: Vec>, }, } @@ -148,7 +163,8 @@ impl Order { use Order::*; match order { Noop => Noop, - DepositAsset { assets, max_assets, beneficiary } => DepositAsset { assets, max_assets, beneficiary }, + DepositAsset { assets, max_assets, beneficiary } => + DepositAsset { assets, max_assets, beneficiary }, DepositReserveAsset { assets, max_assets, dest, effects } => DepositReserveAsset { assets, max_assets, dest, effects }, ExchangeAsset { give, receive } => ExchangeAsset { give, receive }, @@ -157,9 +173,10 @@ impl Order { InitiateTeleport { assets, dest, effects } => InitiateTeleport { assets, dest, effects }, QueryHolding { query_id, dest, assets } => QueryHolding { query_id, dest, assets }, - BuyExecution { fees, weight, debt, halt_on_error, xcm } => { - let xcm = xcm.into_iter().map(Xcm::from).collect(); - BuyExecution { fees, weight, debt, halt_on_error, xcm } + BuyExecution { fees, weight, debt, halt_on_error, orders, instructions } => { + let orders = orders.into_iter().map(Order::from).collect(); + let instructions = instructions.into_iter().map(Xcm::from).collect(); + BuyExecution { fees, weight, debt, halt_on_error, orders, instructions } }, } } diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index 86ad5ccc7720..0f250ddbb954 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -45,7 +45,8 @@ fn weigher_should_work() { weight: 0, debt: 30, halt_on_error: true, - xcm: vec![], + orders: vec![], + instructions: vec![], }, Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here }, ], @@ -117,7 +118,7 @@ fn allow_paid_should_work() { let mut underpaying_message = opaque::Xcm::ReserveAssetDeposited { assets: (X1(Parent), 100).into(), effects: vec![ - Order::BuyExecution { fees, weight: 0, debt: 20, halt_on_error: true, xcm: vec![] }, + Order::BuyExecution { fees, weight: 0, debt: 20, halt_on_error: true, orders: vec![], instructions: vec![] }, Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here }, ], }; @@ -135,7 +136,7 @@ fn allow_paid_should_work() { let mut paying_message = opaque::Xcm::ReserveAssetDeposited { assets: (X1(Parent), 100).into(), effects: vec![ - Order::BuyExecution { fees, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, + Order::BuyExecution { fees, weight: 0, debt: 30, halt_on_error: true, orders: vec![], instructions: vec![] }, Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here }, ], }; @@ -175,9 +176,14 @@ fn paying_reserve_deposit_should_work() { weight: 0, debt: 30, halt_on_error: true, - xcm: vec![], + orders: vec![], + instructions: vec![], + }, + Order::::DepositAsset { + assets: All.into(), + max_assets: 1, + beneficiary: Here, }, - Order::::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here }, ], }; let weight_limit = 50; @@ -222,7 +228,11 @@ fn reserve_transfer_should_work() { Xcm::TransferReserveAsset { assets: (Here, 100).into(), dest: X1(Parachain(2)), - effects: vec![Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: three.clone() }], + effects: vec![Order::DepositAsset { + assets: All.into(), + max_assets: 1, + beneficiary: three.clone(), + }], }, 50, ); @@ -235,7 +245,11 @@ fn reserve_transfer_should_work() { X1(Parachain(2)), Xcm::ReserveAssetDeposited { assets: (X1(Parent), 100).into(), - effects: vec![Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: three }], + effects: vec![Order::DepositAsset { + assets: All.into(), + max_assets: 1, + beneficiary: three + }], } )] ); @@ -303,14 +317,19 @@ fn paid_transacting_should_refund_payment_for_unused_weight() { weight: 70, debt: 30, halt_on_error: true, - xcm: vec![Xcm::::Transact { + orders: vec![], + instructions: vec![Xcm::::Transact { origin_type: OriginKind::Native, require_weight_at_most: 60, // call estimated at 70 but only takes 10. call: TestCall::Any(60, Some(10)).encode().into(), }], }, - Order::::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: one.clone() }, + Order::::DepositAsset { + assets: All.into(), + max_assets: 1, + beneficiary: one.clone(), + }, ], }; let weight_limit = 100; diff --git a/xcm/xcm-builder/src/weight.rs b/xcm/xcm-builder/src/weight.rs index 5e197fee89fa..679aa0928216 100644 --- a/xcm/xcm-builder/src/weight.rs +++ b/xcm/xcm-builder/src/weight.rs @@ -38,21 +38,11 @@ impl, C: Decode + GetDispatchInfo> WeightBounds for FixedWeigh Xcm::WithdrawAsset { effects, .. } | Xcm::ReserveAssetDeposited { effects, .. } | Xcm::ReceiveTeleportedAsset { effects, .. } => { - let inner: Weight = effects - .iter_mut() - .map(|effect| match effect { - Order::BuyExecution { .. } => { - // On success, execution of this will result in more weight being consumed but - // we don't count it here since this is only the *shallow*, non-negotiable weight - // spend and doesn't count weight placed behind a `BuyExecution` since it will not - // be definitely consumed from any existing weight credit if execution of the message - // is attempted. - T::get() - }, - _ => T::get(), - }) - .sum(); - T::get().saturating_add(inner) + let mut extra = T::get(); + for order in effects.iter_mut() { + extra.saturating_accrue(Self::shallow_order(order)?); + } + extra }, _ => T::get(), }) @@ -64,16 +54,43 @@ impl, C: Decode + GetDispatchInfo> WeightBounds for FixedWeigh Xcm::ReserveAssetDeposited { effects, .. } | Xcm::ReceiveTeleportedAsset { effects, .. } => { let mut extra = 0; - for effect in effects.iter_mut() { - match effect { - Order::BuyExecution { xcm, .. } => - for message in xcm.iter_mut() { - extra.saturating_accrue( - Self::shallow(message)?.saturating_add(Self::deep(message)?), - ); - }, - _ => {}, - } + for order in effects.iter_mut() { + extra.saturating_accrue(Self::deep_order(order)?); + } + extra + }, + _ => 0, + }) + } +} + +impl, C: Decode + GetDispatchInfo> FixedWeightBounds { + fn shallow_order(order: &mut Order) -> Result { + Ok(match order { + Order::BuyExecution { .. } => { + // On success, execution of this will result in more weight being consumed but + // we don't count it here since this is only the *shallow*, non-negotiable weight + // spend and doesn't count weight placed behind a `BuyExecution` since it will not + // be definitely consumed from any existing weight credit if execution of the message + // is attempted. + T::get() + }, + _ => T::get(), + }) + } + fn deep_order(order: &mut Order) -> Result { + Ok(match order { + Order::BuyExecution { orders, instructions, .. } => { + let mut extra = 0; + for instruction in instructions.iter_mut() { + extra.saturating_accrue( + Self::shallow(instruction)?.saturating_add(Self::deep(instruction)?), + ); + } + for order in orders.iter_mut() { + extra.saturating_accrue( + Self::shallow_order(order)?.saturating_add(Self::deep_order(order)?), + ); } extra }, diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 6a939935b46d..f90e4b0a0e03 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -240,26 +240,27 @@ impl Assets { ) -> Result { let mut taken = Assets::new(); match mask { - MultiAssetFilter::Wild(All) => if self.fungible.len() + self.non_fungible.len() <= limit { - return Ok(self.swapped(Assets::new())) - } else { - let fungible = mem::replace(&mut self.fungible, Default::default()); - fungible.into_iter().for_each(|(c, amount)| { - if taken.len() < limit { - taken.fungible.insert(c, amount); - } else { - self.fungible.insert(c, amount); - } - }); - let non_fungible = mem::replace(&mut self.non_fungible, Default::default()); - non_fungible.into_iter().for_each(|(c, instance)| { - if taken.len() < limit { - taken.non_fungible.insert((c, instance)); - } else { - self.non_fungible.insert((c, instance)); - } - }); - }, + MultiAssetFilter::Wild(All) => + if self.fungible.len() + self.non_fungible.len() <= limit { + return Ok(self.swapped(Assets::new())) + } else { + let fungible = mem::replace(&mut self.fungible, Default::default()); + fungible.into_iter().for_each(|(c, amount)| { + if taken.len() < limit { + taken.fungible.insert(c, amount); + } else { + self.fungible.insert(c, amount); + } + }); + let non_fungible = mem::replace(&mut self.non_fungible, Default::default()); + non_fungible.into_iter().for_each(|(c, instance)| { + if taken.len() < limit { + taken.non_fungible.insert((c, instance)); + } else { + self.non_fungible.insert((c, instance)); + } + }); + }, MultiAssetFilter::Wild(AllOf { fun: WildFungible, id }) => { if let Some((id, amount)) = self.fungible.remove_entry(&id) { taken.fungible.insert(id, amount); diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index e59aa54a1fd8..88fe03ad27d7 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -247,28 +247,28 @@ impl XcmExecutor { if let Some((mut holding, effects)) = maybe_holding_effects { for effect in effects.into_iter() { - total_surplus += Self::execute_effects(&origin, &mut holding, effect, trader)?; + total_surplus += Self::execute_orders(&origin, &mut holding, effect, trader)?; } } Ok(total_surplus) } - fn execute_effects( + fn execute_orders( origin: &MultiLocation, holding: &mut Assets, - effect: Order, + order: Order, trader: &mut Config::Trader, ) -> Result { log::trace!( - target: "xcm::execute_effects", + target: "xcm::execute_orders", "origin: {:?}, holding: {:?}, effect: {:?}", origin, holding, - effect, + order, ); let mut total_surplus = 0; - match effect { + match order { Order::DepositAsset { assets, max_assets, beneficiary } => { let deposited = holding.limited_saturating_take(assets, max_assets as usize); for asset in deposited.into_assets_iter() { @@ -303,7 +303,7 @@ impl XcmExecutor { Xcm::QueryResponse { query_id, response: Response::Assets(assets) }, )?; }, - Order::BuyExecution { fees, weight, debt, halt_on_error, xcm } => { + Order::BuyExecution { fees, weight, debt, halt_on_error, orders, instructions } => { // pay for `weight` using up to `fees` of the holding register. let purchasing_weight = Weight::from(weight.checked_add(debt).ok_or(XcmError::Overflow)?); @@ -313,11 +313,23 @@ impl XcmExecutor { holding.subsume_assets(unspent); let mut remaining_weight = weight; - for message in xcm.into_iter() { + for order in orders.into_iter() { + match Self::execute_orders( + origin, + holding, + order, + trader, + ) { + Err(e) if halt_on_error => return Err(e), + Err(_) => {}, + Ok(surplus) => total_surplus += surplus, + } + } + for instruction in instructions.into_iter() { match Self::do_execute_xcm( origin.clone(), false, - message, + instruction, &mut remaining_weight, None, trader, From 851b00e8139c8b2762ff82843a722d207caac3a4 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 17:00:05 +0200 Subject: [PATCH 75/84] Fix XCM extrinsics --- runtime/kusama/src/lib.rs | 1 + runtime/rococo/src/lib.rs | 1 + runtime/westend/src/lib.rs | 1 + xcm/pallet-xcm/src/lib.rs | 36 +++++++++++++++----- xcm/pallet-xcm/src/mock.rs | 5 +-- xcm/pallet-xcm/src/tests.rs | 10 +++--- xcm/src/v1/multiasset.rs | 16 +++++++++ xcm/xcm-builder/src/tests.rs | 18 ++++++++-- xcm/xcm-executor/src/lib.rs | 7 +--- xcm/xcm-simulator/example/src/lib.rs | 6 ++-- xcm/xcm-simulator/example/src/parachain.rs | 1 + xcm/xcm-simulator/example/src/relay_chain.rs | 1 + 12 files changed, 78 insertions(+), 25 deletions(-) diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index 6fc6b8ac3524..32a5455b8d13 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -1323,6 +1323,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; } parameter_types! { diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index f05de3d26771..c82e777f7f02 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -695,6 +695,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; } impl parachains_session_info::Config for Runtime {} diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index d0304022c602..9944efa1f5f4 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -956,6 +956,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; } construct_runtime! { diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index bbbcfe1163b3..779898a604bb 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -39,7 +39,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use sp_runtime::traits::AccountIdConversion; - use xcm_executor::traits::WeightBounds; + use xcm_executor::traits::{WeightBounds, InvertLocation}; #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] @@ -77,6 +77,9 @@ pub mod pallet { /// Means of measuring the weight consumed by an XCM message locally. type Weigher: WeightBounds; + + /// Means of inverting a location. + type LocationInverter: InvertLocation; } #[pallet::event] @@ -96,6 +99,8 @@ pub mod pallet { UnweighableMessage, /// The assets to be sent are empty. Empty, + /// Could not reanchor the assets to declare the fees for the destination chain. + CannotReanchor, } #[pallet::hooks] @@ -143,13 +148,20 @@ pub mod pallet { dest: MultiLocation, beneficiary: MultiLocation, assets: MultiAssets, + fee_asset_item: u32, dest_weight: Weight, ) -> DispatchResult { let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; let value = (origin_location, assets.drain()); ensure!(T::XcmTeleportFilter::contains(&value), Error::::Filtered); let (origin_location, assets) = value; - let fees = assets.first().ok_or(Error::::Empty)?.clone(); + let inv_dest = T::LocationInverter::invert_location(&dest); + let fees = assets.get(fee_asset_item as usize) + .ok_or(Error::::Empty)? + .clone() + .reanchored(&inv_dest) + .map_err(|_| Error::::CannotReanchor)?; + let max_assets = assets.len() as u32; let assets = assets.into(); let mut message = Xcm::WithdrawAsset { assets, @@ -166,7 +178,7 @@ pub mod pallet { orders: vec![], instructions: vec![], }, - DepositAsset { assets: Wild(All), max_assets: 1, beneficiary }, + DepositAsset { assets: Wild(All), max_assets, beneficiary }, ], }], }; @@ -203,13 +215,21 @@ pub mod pallet { dest: MultiLocation, beneficiary: MultiLocation, assets: MultiAssets, + fee_asset_item: u32, dest_weight: Weight, ) -> DispatchResult { let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; let value = (origin_location, assets.drain()); ensure!(T::XcmReserveTransferFilter::contains(&value), Error::::Filtered); - let (origin_location, assets) = value; - let fees = assets.first().ok_or(Error::::Empty)?.clone(); + let (origin_location, mut assets) = value; + let inv_dest = T::LocationInverter::invert_location(&dest); + let fees = assets.get(fee_asset_item as usize) + .ok_or(Error::::Empty)? + .clone() + .reanchored(&inv_dest) + .map_err(|_| Error::::CannotReanchor)?; + let max_assets = assets.len() as u32; + assets.sort(); let assets = assets.into(); let mut message = Xcm::TransferReserveAsset { assets, @@ -217,14 +237,14 @@ pub mod pallet { effects: vec![ BuyExecution { fees, - // Zero weight for additional XCM (since there are none to execute) + // Zero weight for additional instructions/orders (since there are none to execute) weight: 0, - debt: dest_weight, + debt: dest_weight, // covers this, `TransferReserveAsset` xcm, and `DepositAsset` order. halt_on_error: false, orders: vec![], instructions: vec![], }, - DepositAsset { assets: Wild(All), max_assets: 1, beneficiary }, + DepositAsset { assets: Wild(All), max_assets, beneficiary }, ], }; let weight = diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index 50a2471d74d7..ca143ce11db0 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -186,6 +186,7 @@ impl pallet_xcm::Config for Test { type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; } impl origin::Config for Test {} @@ -194,10 +195,10 @@ pub(crate) fn last_event() -> Event { System::events().pop().expect("Event expected").event } -pub(crate) fn buy_execution(debt: Weight) -> Order { +pub(crate) fn buy_execution(fees: impl Into, debt: Weight) -> Order { use xcm::opaque::v1::prelude::*; Order::BuyExecution { - fees: (RelayLocation::get(), 10).into(), + fees: fees.into(), weight: 0, debt, halt_on_error: false, diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index dd86ca3a05e0..f10e0490484d 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -42,7 +42,7 @@ fn send_works() { let message = Xcm::ReserveAssetDeposited { assets: (X1(Parent), SEND_AMOUNT).into(), effects: vec![ - buy_execution(weight), + buy_execution((Parent, SEND_AMOUNT), weight), DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() }, ], }; @@ -76,7 +76,7 @@ fn send_fails_when_xcm_router_blocks() { let message = Xcm::ReserveAssetDeposited { assets: (Parent, SEND_AMOUNT).into(), effects: vec![ - buy_execution(weight), + buy_execution((Parent, SEND_AMOUNT), weight), DepositAsset { assets: All.into(), max_assets: 1, beneficiary: sender.clone() }, ], }; @@ -116,6 +116,7 @@ fn teleport_assets_works() { RelayLocation::get(), X1(AccountId32 { network: Any, id: BOB.into() }), (Here, SEND_AMOUNT).into(), + 0, weight, )); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT); @@ -144,6 +145,7 @@ fn reserve_transfer_assets_works() { Parachain(PARA_ID).into(), dest.clone(), (Here, SEND_AMOUNT).into(), + 0, weight )); // Alice spent amount @@ -158,7 +160,7 @@ fn reserve_transfer_assets_works() { Xcm::ReserveAssetDeposited { assets: (X1(Parent), SEND_AMOUNT).into(), effects: vec![ - buy_execution(weight), + buy_execution((Parent, SEND_AMOUNT), weight), DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest }, ] } @@ -189,7 +191,7 @@ fn execute_withdraw_to_deposit_works() { Box::new(Xcm::WithdrawAsset { assets: (Here, SEND_AMOUNT).into(), effects: vec![ - buy_execution(weight), + buy_execution((Here, SEND_AMOUNT), weight), DepositAsset { assets: All.into(), max_assets: 1, beneficiary: dest } ], }), diff --git a/xcm/src/v1/multiasset.rs b/xcm/src/v1/multiasset.rs index cc0672b2d2d8..dc3c4641e232 100644 --- a/xcm/src/v1/multiasset.rs +++ b/xcm/src/v1/multiasset.rs @@ -217,6 +217,12 @@ impl MultiAsset { self.id.reanchor(prepend) } + /// Prepend a `MultiLocation` to a concrete asset, giving it a new root location. + pub fn reanchored(mut self, prepend: &MultiLocation) -> Result { + self.reanchor(prepend)?; + Ok(self) + } + /// Returns true if `self` is a super-set of the given `inner`. pub fn contains(&self, inner: &MultiAsset) -> bool { use Fungibility::*; @@ -307,10 +313,20 @@ impl MultiAssets { &self.0 } + /// Return the number of distinct asset instances contained. + pub fn len(&self) -> usize { + self.0.len() + } + /// Prepend a `MultiLocation` to any concrete asset items, giving it a new root location. pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> { self.0.iter_mut().try_for_each(|i| i.reanchor(prepend)) } + + /// Return a reference to an item at a specific index or `None` if it doesn't exist. + pub fn get(&self, index: usize) -> Option<&MultiAsset> { + self.0.get(index) + } } /// Classification of whether an asset is fungible or not, along with an optional amount or instance. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)] diff --git a/xcm/xcm-builder/src/tests.rs b/xcm/xcm-builder/src/tests.rs index 0f250ddbb954..8f95687f29cf 100644 --- a/xcm/xcm-builder/src/tests.rs +++ b/xcm/xcm-builder/src/tests.rs @@ -118,7 +118,14 @@ fn allow_paid_should_work() { let mut underpaying_message = opaque::Xcm::ReserveAssetDeposited { assets: (X1(Parent), 100).into(), effects: vec![ - Order::BuyExecution { fees, weight: 0, debt: 20, halt_on_error: true, orders: vec![], instructions: vec![] }, + Order::BuyExecution { + fees, + weight: 0, + debt: 20, + halt_on_error: true, + orders: vec![], + instructions: vec![], + }, Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here }, ], }; @@ -136,7 +143,14 @@ fn allow_paid_should_work() { let mut paying_message = opaque::Xcm::ReserveAssetDeposited { assets: (X1(Parent), 100).into(), effects: vec![ - Order::BuyExecution { fees, weight: 0, debt: 30, halt_on_error: true, orders: vec![], instructions: vec![] }, + Order::BuyExecution { + fees, + weight: 0, + debt: 30, + halt_on_error: true, + orders: vec![], + instructions: vec![], + }, Order::DepositAsset { assets: All.into(), max_assets: 1, beneficiary: Here }, ], }; diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 88fe03ad27d7..c30c5d3f9a77 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -314,12 +314,7 @@ impl XcmExecutor { let mut remaining_weight = weight; for order in orders.into_iter() { - match Self::execute_orders( - origin, - holding, - order, - trader, - ) { + match Self::execute_orders(origin, holding, order, trader) { Err(e) if halt_on_error => return Err(e), Err(_) => {}, Ok(surplus) => total_surplus += surplus, diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 12e39a51e30f..a02aeaaff456 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -17,10 +17,9 @@ mod parachain; mod relay_chain; -use sp_runtime::AccountId32; use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; -pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]); +pub const ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([0u8; 32]); decl_test_parachain! { pub struct ParaA { @@ -194,7 +193,8 @@ mod tests { X1(Parachain(1)), X1(AccountId32 { network: Any, id: ALICE.into() }), (Here, 123).into(), - 123, + 0, + 3, )); }); diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index 4e870abb0315..3e7212c48fa4 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -293,6 +293,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = (); type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; } type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index e691e3e7ad35..adbf0b965f41 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -147,6 +147,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; } parameter_types! { From 20bb7140a0eec64d2416d3dd0fc9e0fdc2283db2 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 17:04:34 +0200 Subject: [PATCH 76/84] fmt --- xcm/pallet-xcm/src/lib.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index 779898a604bb..b6742e9b58d0 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -39,7 +39,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use sp_runtime::traits::AccountIdConversion; - use xcm_executor::traits::{WeightBounds, InvertLocation}; + use xcm_executor::traits::{InvertLocation, WeightBounds}; #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] @@ -156,7 +156,8 @@ pub mod pallet { ensure!(T::XcmTeleportFilter::contains(&value), Error::::Filtered); let (origin_location, assets) = value; let inv_dest = T::LocationInverter::invert_location(&dest); - let fees = assets.get(fee_asset_item as usize) + let fees = assets + .get(fee_asset_item as usize) .ok_or(Error::::Empty)? .clone() .reanchored(&inv_dest) @@ -223,7 +224,8 @@ pub mod pallet { ensure!(T::XcmReserveTransferFilter::contains(&value), Error::::Filtered); let (origin_location, mut assets) = value; let inv_dest = T::LocationInverter::invert_location(&dest); - let fees = assets.get(fee_asset_item as usize) + let fees = assets + .get(fee_asset_item as usize) .ok_or(Error::::Empty)? .clone() .reanchored(&inv_dest) @@ -239,7 +241,7 @@ pub mod pallet { fees, // Zero weight for additional instructions/orders (since there are none to execute) weight: 0, - debt: dest_weight, // covers this, `TransferReserveAsset` xcm, and `DepositAsset` order. + debt: dest_weight, // covers this, `TransferReserveAsset` xcm, and `DepositAsset` order. halt_on_error: false, orders: vec![], instructions: vec![], From 3f85ebd8de67c8bc27a2cefceb1e72bac5fad608 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 17:58:34 +0200 Subject: [PATCH 77/84] Make Vec/MultiAssets conversions safe --- xcm/src/v1/multiasset.rs | 66 +++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/xcm/src/v1/multiasset.rs b/xcm/src/v1/multiasset.rs index dc3c4641e232..ea7d17212749 100644 --- a/xcm/src/v1/multiasset.rs +++ b/xcm/src/v1/multiasset.rs @@ -243,27 +243,40 @@ pub struct MultiAssets(Vec); impl Decode for MultiAssets { fn decode(input: &mut I) -> Result { - let r = Vec::::decode(input)?; - if r.is_empty() { - return Ok(Self(Vec::new())) - } - r.iter().skip(1).try_fold( - &r[0], - |a, b| -> Result<&MultiAsset, parity_scale_codec::Error> { - if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) { - Ok(b) - } else { - Err("Out of order".into()) - } - }, - )?; - Ok(Self(r)) + Self::from_sorted_and_deduplicated(Vec::::decode(input)?) + .map_err(|()| "Out of order".into()) } } impl From> for MultiAssets { - fn from(x: Vec) -> Self { - Self(x) + fn from(mut assets: Vec) -> Self { + let mut res = Vec::with_capacity(assets.len()); + if !assets.is_empty() { + assets.sort(); + let mut iter = assets.into_iter(); + if let Some(first) = iter.next() { + let last = iter.fold(first, |a, b| -> MultiAsset { + match (a, b) { + ( + MultiAsset { fun: Fungibility::Fungible(a_amount), id: a_id }, + MultiAsset { fun: Fungibility::Fungible(b_amount), id: b_id }, + ) if a_id == b_id => + MultiAsset { id: a_id, fun: Fungibility::Fungible(a_amount + b_amount) }, + ( + MultiAsset { fun: Fungibility::NonFungible(a_instance), id: a_id }, + MultiAsset { fun: Fungibility::NonFungible(b_instance), id: b_id }, + ) if a_id == b_id && a_instance == b_instance => + MultiAsset { fun: Fungibility::NonFungible(a_instance), id: a_id }, + (to_push, to_remember) => { + res.push(to_push); + to_remember + }, + } + }); + res.push(last); + } + } + Self(res) } } @@ -279,6 +292,25 @@ impl MultiAssets { Self(Vec::new()) } + /// Create a new instance of `MultiAssets` from a `Vec` whose contents are sorted and + /// which contain no duplicates. + /// + /// Returns `Ok` if the operation succeeds and `Err` if `r` is out of order or had duplicates. If you can't + /// guarantee that `r` is sorted and deduplicated, then use `From::>::from` which is infallible. + pub fn from_sorted_and_deduplicated(r: Vec) -> Result { + if r.is_empty() { + return Ok(Self(Vec::new())) + } + r.iter().skip(1).try_fold(&r[0], |a, b| -> Result<&MultiAsset, ()> { + if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) { + Ok(b) + } else { + Err(()) + } + })?; + Ok(Self(r)) + } + /// Add some asset onto the list. This is quite a laborious operation since it maintains the ordering. pub fn push(&mut self, a: MultiAsset) { if let Fungibility::Fungible(ref amount) = a.fun { From 61a2231c2fba6bc9579e27b24076dd8a4f543f7a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 18:07:41 +0200 Subject: [PATCH 78/84] More MultiAssets conversion safety --- xcm/src/v1/multiasset.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/xcm/src/v1/multiasset.rs b/xcm/src/v1/multiasset.rs index ea7d17212749..7b90b8581984 100644 --- a/xcm/src/v1/multiasset.rs +++ b/xcm/src/v1/multiasset.rs @@ -311,6 +311,27 @@ impl MultiAssets { Ok(Self(r)) } + /// Create a new instance of `MultiAssets` from a `Vec` whose contents are sorted and + /// which contain no duplicates. + /// + /// In release mode, this skips any checks to ensure that `r` is correct, making it a negligible-cost operation. + /// Generally though you should avoid using it unless you have a strict proof that `r` is valid. + #[cfg(test)] + pub fn from_sorted_and_deduplicated_skip_checks(r: Vec) -> Self { + Self::from_sorted_and_deduplicated(r).expect("Invalid input r is not sorted/deduped") + } + /// Create a new instance of `MultiAssets` from a `Vec` whose contents are sorted and + /// which contain no duplicates. + /// + /// In release mode, this skips any checks to ensure that `r` is correct, making it a negligible-cost operation. + /// Generally though you should avoid using it unless you have a strict proof that `r` is valid. + /// + /// In test mode, this checks anyway and panics on fail. + #[cfg(not(test))] + pub fn from_sorted_and_deduplicated_skip_checks(r: Vec) -> Self { + Self(r) + } + /// Add some asset onto the list. This is quite a laborious operation since it maintains the ordering. pub fn push(&mut self, a: MultiAsset) { if let Fungibility::Fungible(ref amount) = a.fun { From 86442655ad27960910cee7e6ed5ec64977eb4849 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 19:12:37 +0200 Subject: [PATCH 79/84] spelling --- xcm/src/v1/order.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xcm/src/v1/order.rs b/xcm/src/v1/order.rs index 6a2b04e9acf3..f311b5f9bf05 100644 --- a/xcm/src/v1/order.rs +++ b/xcm/src/v1/order.rs @@ -36,7 +36,7 @@ pub enum Order { /// /// - `assets`: The asset(s) to remove from holding. /// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. Only the first - /// `max_assets` assets/instances of those matched by `assets` will be removed, prioritised under standard asset + /// `max_assets` assets/instances of those matched by `assets` will be removed, prioritized under standard asset /// ordering. Any others will remain in holding. /// - `beneficiary`: The new owner for the assets. /// @@ -51,7 +51,7 @@ pub enum Order { /// /// - `assets`: The asset(s) to remove from holding. /// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. Only the first - /// `max_assets` assets/instances of those matched by `assets` will be removed, prioritised under standard asset + /// `max_assets` assets/instances of those matched by `assets` will be removed, prioritized under standard asset /// ordering. Any others will remain in holding. /// - `dest`: The location whose sovereign account will own the assets and thus the effective beneficiary for the /// assets and the notification target for the reserve asset deposit message. From 1e75953246dab079422496ad8de9659d2a483f6f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 4 Aug 2021 19:14:40 +0200 Subject: [PATCH 80/84] fix doc test --- xcm/xcm-executor/src/assets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index f90e4b0a0e03..212f60cdc9b6 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -378,7 +378,7 @@ impl Assets { /// use xcm_executor::Assets; /// use xcm::v1::prelude::*; /// let assets_i_have: Assets = vec![ (Here, 100).into(), (vec![0], 100).into() ].into(); - /// let assets_they_want: MultiAssetFilter = vec![ (Here, 200).into(), (vec![0], 50).into() [.into(); + /// let assets_they_want: MultiAssetFilter = vec![ (Here, 200).into(), (vec![0], 50).into() ].into(); /// /// let assets_we_can_trade: Assets = assets_i_have.min(&assets_they_want); /// assert_eq!(assets_we_can_trade.into_assets_iter().collect::>(), vec![ From 669dcb0ff9f441267c3fd2cbb3066c7f1c863ca6 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Thu, 5 Aug 2021 11:14:39 +0200 Subject: [PATCH 81/84] Apply suggestions from code review Co-authored-by: Amar Singh --- xcm/src/lib.rs | 2 +- xcm/src/v1/multiasset.rs | 10 ++-------- xcm/src/v1/order.rs | 2 +- xcm/xcm-executor/src/assets.rs | 4 ++++ 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/xcm/src/lib.rs b/xcm/src/lib.rs index e7d31a2a7d74..d7d318225200 100644 --- a/xcm/src/lib.rs +++ b/xcm/src/lib.rs @@ -73,7 +73,7 @@ impl TryFrom> for v1::Xcm { pub mod opaque { pub mod v1 { - // Everything from v0 + // Everything from v1 pub use crate::v1::*; // Then override with the opaque types in v0 pub use crate::v1::opaque::{Order, Xcm}; diff --git a/xcm/src/v1/multiasset.rs b/xcm/src/v1/multiasset.rs index 7b90b8581984..61a82b7c32e1 100644 --- a/xcm/src/v1/multiasset.rs +++ b/xcm/src/v1/multiasset.rs @@ -34,7 +34,7 @@ use parity_scale_codec::{self as codec, Decode, Encode}; /// A general identifier for an instance of a non-fungible asset class. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)] pub enum AssetInstance { - /// Undefined - used if the NFA class has only one instance. + /// Undefined - used if the non-fungible asset class has only one instance. Undefined, /// A compact index. Technically this could be greater than `u128`, but this implementation supports only @@ -63,12 +63,6 @@ impl From<()> for AssetInstance { } } -/*impl From for AssetInstance { - fn from(x: u128) -> Self { - Self::Index(x) - } -}*/ - impl From<[u8; 4]> for AssetInstance { fn from(x: [u8; 4]) -> Self { Self::Array4(x) @@ -381,7 +375,7 @@ impl MultiAssets { self.0.get(index) } } -/// Classification of whether an asset is fungible or not, along with an optional amount or instance. +/// Classification of whether an asset is fungible or not. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)] pub enum WildFungibility { Fungible, diff --git a/xcm/src/v1/order.rs b/xcm/src/v1/order.rs index f311b5f9bf05..0c92e5c4ce8c 100644 --- a/xcm/src/v1/order.rs +++ b/xcm/src/v1/order.rs @@ -130,7 +130,7 @@ pub enum Order { /// /// - `fees`: The asset(s) to remove from holding to pay for fees. /// - `weight`: The amount of weight to purchase; this should be at least the shallow weight of `effects` and `xcm`. - /// - `debt`: The amount of weight-debt already incurred to be pay off; this should be equal to the unpaid weight of + /// - `debt`: The amount of weight-debt already incurred to be paid off; this should be equal to the unpaid weight of /// any surrounding operations/orders. /// - `halt_on_error`: If `true`, the execution of the `orders` and `operations` will halt on the first failure. If /// `false`, then execution will continue regardless. diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index 212f60cdc9b6..adc48b899d23 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -428,18 +428,22 @@ mod tests { use xcm::v1::prelude::*; use MultiLocation::Here; #[allow(non_snake_case)] + /// Abstract fungible constructor fn AF(id: u8, amount: u128) -> MultiAsset { (vec![id], amount).into() } #[allow(non_snake_case)] + /// Abstract non-fungible constructor fn ANF(class: u8, instance_id: u8) -> MultiAsset { (vec![class], vec![instance_id]).into() } #[allow(non_snake_case)] + /// Concrete fungible constructor fn CF(amount: u128) -> MultiAsset { (Here, amount).into() } #[allow(non_snake_case)] + /// Concrete non-fungible constructor fn CNF(instance_id: u8) -> MultiAsset { (Here, [instance_id; 4]).into() } From 767bdb597970201712757e5149f81e4e44ea4490 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Thu, 5 Aug 2021 11:17:20 +0200 Subject: [PATCH 82/84] Apply suggestions from code review Co-authored-by: Amar Singh --- xcm/src/lib.rs | 2 +- xcm/src/v1/mod.rs | 2 +- xcm/src/v1/multiasset.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/xcm/src/lib.rs b/xcm/src/lib.rs index d7d318225200..3addd73421e5 100644 --- a/xcm/src/lib.rs +++ b/xcm/src/lib.rs @@ -75,7 +75,7 @@ pub mod opaque { pub mod v1 { // Everything from v1 pub use crate::v1::*; - // Then override with the opaque types in v0 + // Then override with the opaque types in v1 pub use crate::v1::opaque::{Order, Xcm}; } diff --git a/xcm/src/v1/mod.rs b/xcm/src/v1/mod.rs index c27bb1788069..c78506c5c042 100644 --- a/xcm/src/v1/mod.rs +++ b/xcm/src/v1/mod.rs @@ -148,7 +148,7 @@ pub enum Xcm { /// - `assets`: The asset(s) that are minted into the Holding Register. /// - `effects`: The order(s) to execute on the Holding Register. /// - /// Safety: `origin` must be trusted to have irrevocably destroyed the corresponding`assets` prior as a consequence + /// Safety: `origin` must be trusted to have irrevocably destroyed the corresponding `assets` prior as a consequence /// of sending this message. /// /// Kind: *Trusted Indication*. diff --git a/xcm/src/v1/multiasset.rs b/xcm/src/v1/multiasset.rs index 61a82b7c32e1..a52725a58c1a 100644 --- a/xcm/src/v1/multiasset.rs +++ b/xcm/src/v1/multiasset.rs @@ -386,7 +386,7 @@ pub enum WildFungibility { #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode)] pub enum WildMultiAsset { /// All assets in the holding register, up to `usize` individual assets (different instances of non-fungibles could - /// as separate assets). + /// be separate assets). All, /// All assets in the holding register of a given fungibility and ID. If operating on non-fungibles, then a limit /// is provided for the maximum amount of matching instances. From 410266e9e2e74c6eaebc99ef27fd2ee92e39e364 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Thu, 5 Aug 2021 12:57:10 +0200 Subject: [PATCH 83/84] fmt --- xcm/xcm-executor/src/assets.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index adc48b899d23..f04483f1a6fa 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -428,22 +428,22 @@ mod tests { use xcm::v1::prelude::*; use MultiLocation::Here; #[allow(non_snake_case)] - /// Abstract fungible constructor + /// Abstract fungible constructor fn AF(id: u8, amount: u128) -> MultiAsset { (vec![id], amount).into() } #[allow(non_snake_case)] - /// Abstract non-fungible constructor + /// Abstract non-fungible constructor fn ANF(class: u8, instance_id: u8) -> MultiAsset { (vec![class], vec![instance_id]).into() } #[allow(non_snake_case)] - /// Concrete fungible constructor + /// Concrete fungible constructor fn CF(amount: u128) -> MultiAsset { (Here, amount).into() } #[allow(non_snake_case)] - /// Concrete non-fungible constructor + /// Concrete non-fungible constructor fn CNF(instance_id: u8) -> MultiAsset { (Here, [instance_id; 4]).into() } From ab8c01b51a99679a929d2166663cb27b69cf02f7 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Thu, 5 Aug 2021 13:45:30 +0200 Subject: [PATCH 84/84] nit --- xcm/xcm-simulator/example/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 9bbfbc945874..7b55502703a9 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -225,7 +225,7 @@ mod tests { relay_chain::Origin::signed(ALICE), X1(Parachain(1)), X1(AccountId32 { network: Any, id: ALICE.into() }), - (Here, 123).into(), + (Here, withdraw_amount).into(), 0, max_weight_for_execution, ));