diff --git a/Cargo.lock b/Cargo.lock index aa4994e9a..40b4ce282 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13745,6 +13745,8 @@ dependencies = [ [[package]] name = "staging-xcm-executor" version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5b83ea34a2ba2083c6f5bfec468fb00535d0e0788a78237d06da32dba76be9" dependencies = [ "environmental", "frame-benchmarking", diff --git a/Cargo.toml b/Cargo.toml index dd9ae38ef..2759b888b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,6 @@ members = [ "macros", "macros/tests", "polimec-common/*", - "debug-executor", ] default-members = ["nodes/*", "pallets/*"] resolver = "2" @@ -249,6 +248,3 @@ cumulus-pallet-session-benchmarking = { version = "16.0.0", default-features = f polimec-runtime = { path = "runtimes/polimec" } rococo-runtime-constants = { version = "14.0.0" } rococo-runtime = { version = "14.0.0" } - -[patch.crates-io] -xcm-executor = { path = "./debug-executor", package = "staging-xcm-executor", default-features = false } \ No newline at end of file diff --git a/debug-executor/.cargo-ok b/debug-executor/.cargo-ok deleted file mode 100644 index 5f8b79583..000000000 --- a/debug-executor/.cargo-ok +++ /dev/null @@ -1 +0,0 @@ -{"v":1} \ No newline at end of file diff --git a/debug-executor/Cargo.toml b/debug-executor/Cargo.toml deleted file mode 100644 index 2a7f1502b..000000000 --- a/debug-executor/Cargo.toml +++ /dev/null @@ -1,204 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies. -# -# If you are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. - -[package] -edition = "2021" -name = "staging-xcm-executor" -version = "14.0.0" -authors = ["Parity Technologies "] -description = "An abstract and configurable XCM message executor. (polkadot v1.13.0)" -license = "GPL-3.0-only" - -[dependencies.codec] -version = "3.6.12" -features = ["derive"] -default-features = false -package = "parity-scale-codec" - -[dependencies.environmental] -version = "1.1.4" -default-features = false - -[dependencies.frame-benchmarking] -version = "35.0.0" -optional = true -default-features = false - -[dependencies.frame-support] -version = "35.0.0" -default-features = false - -[dependencies.impl-trait-for-tuples] -version = "0.2.2" - -[dependencies.log] -version = "0.4.21" -default-features = false - -[dependencies.scale-info] -version = "2.11.1" -features = [ - "derive", - "serde", -] -default-features = false - -[dependencies.sp-arithmetic] -version = "26.0.0" -default-features = false - -[dependencies.sp-core] -version = "34.0.0" -default-features = false - -[dependencies.sp-io] -version = "37.0.0" -default-features = false - -[dependencies.sp-runtime] -version = "38.0.0" -default-features = false - -[dependencies.sp-std] -version = "14.0.0" -default-features = false - -[dependencies.sp-weights] -version = "31.0.0" -default-features = false - -[dependencies.xcm] -version = "14.0.0" -default-features = false -package = "staging-xcm" - -[features] -default = ["std"] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -std = [ - "codec/std", - "environmental/std", - "frame-benchmarking/std", - "frame-support/std", - "log/std", - "scale-info/std", - "sp-arithmetic/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "sp-std/std", - "sp-weights/std", - "xcm/std", -] - -[lints.clippy.all] -level = "allow" -priority = 0 - -[lints.clippy.bind_instead_of_map] -level = "allow" -priority = 2 - -[lints.clippy.borrowed-box] -level = "allow" -priority = 2 - -[lints.clippy.complexity] -level = "warn" -priority = 1 - -[lints.clippy.correctness] -level = "warn" -priority = 1 - -[lints.clippy.default_constructed_unit_structs] -level = "allow" -priority = 2 - -[lints.clippy.derivable_impls] -level = "allow" -priority = 2 - -[lints.clippy.eq_op] -level = "allow" -priority = 2 - -[lints.clippy.erasing_op] -level = "allow" -priority = 2 - -[lints.clippy.extra-unused-type-parameters] -level = "allow" -priority = 2 - -[lints.clippy.identity-op] -level = "allow" -priority = 2 - -[lints.clippy.if-same-then-else] -level = "allow" -priority = 2 - -[lints.clippy.needless-lifetimes] -level = "allow" -priority = 2 - -[lints.clippy.needless_option_as_deref] -level = "allow" -priority = 2 - -[lints.clippy.nonminimal-bool] -level = "allow" -priority = 2 - -[lints.clippy.option-map-unit-fn] -level = "allow" -priority = 2 - -[lints.clippy.stable_sort_primitive] -level = "allow" -priority = 2 - -[lints.clippy.too-many-arguments] -level = "allow" -priority = 2 - -[lints.clippy.type_complexity] -level = "allow" -priority = 2 - -[lints.clippy.unit_arg] -level = "allow" -priority = 2 - -[lints.clippy.unnecessary_cast] -level = "allow" -priority = 2 - -[lints.clippy.useless_conversion] -level = "allow" -priority = 2 - -[lints.clippy.while_immutable_condition] -level = "allow" -priority = 2 - -[lints.clippy.zero-prefixed-literal] -level = "allow" -priority = 2 - -[lints.rust.suspicious_double_ref_op] -level = "allow" -priority = 2 diff --git a/debug-executor/Cargo.toml.orig b/debug-executor/Cargo.toml.orig deleted file mode 100644 index 29917f026..000000000 --- a/debug-executor/Cargo.toml.orig +++ /dev/null @@ -1,49 +0,0 @@ -[package] -name = "staging-xcm-executor" -description = "An abstract and configurable XCM message executor. (polkadot v1.13.0)" -authors.workspace = true -edition.workspace = true -license.workspace = true -version = "14.0.0" - -[lints] -workspace = true - -[dependencies] -impl-trait-for-tuples = "0.2.2" -environmental = { version = "1.1.4", default-features = false } -codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false, features = ["derive"] } -scale-info = { version = "2.11.1", default-features = false, features = ["derive", "serde"] } -xcm = { package = "staging-xcm", path = "..", default-features = false, version = "14.0.0" } -sp-std = { path = "../../../substrate/primitives/std", default-features = false, version = "14.0.0" } -sp-io = { path = "../../../substrate/primitives/io", default-features = false, version = "37.0.0" } -sp-arithmetic = { path = "../../../substrate/primitives/arithmetic", default-features = false, version = "26.0.0" } -sp-core = { path = "../../../substrate/primitives/core", default-features = false, version = "34.0.0" } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false, version = "38.0.0" } -sp-weights = { path = "../../../substrate/primitives/weights", default-features = false, version = "31.0.0" } -frame-support = { path = "../../../substrate/frame/support", default-features = false, version = "35.0.0" } -log = { workspace = true } -frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true, version = "35.0.0" } - -[features] -default = ["std"] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -std = [ - "codec/std", - "environmental/std", - "frame-benchmarking/std", - "frame-support/std", - "log/std", - "scale-info/std", - "sp-arithmetic/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "sp-std/std", - "sp-weights/std", - "xcm/std", -] diff --git a/debug-executor/src/assets.rs b/debug-executor/src/assets.rs deleted file mode 100644 index 96906b712..000000000 --- a/debug-executor/src/assets.rs +++ /dev/null @@ -1,695 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use sp_runtime::{traits::Saturating, RuntimeDebug}; -use sp_std::{ - collections::{btree_map::BTreeMap, btree_set::BTreeSet}, - mem, - prelude::*, -}; -use xcm::latest::{ - Asset, AssetFilter, AssetId, AssetInstance, Assets, - Fungibility::{Fungible, NonFungible}, - InteriorLocation, Location, Reanchorable, - WildAsset::{All, AllCounted, AllOf, AllOfCounted}, - WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible}, -}; - -/// Map of non-wildcard fungible and non-fungible assets held in the holding register. -#[derive(Default, Clone, RuntimeDebug, Eq, PartialEq)] -pub struct AssetsInHolding { - /// The fungible assets. - pub fungible: BTreeMap, - - /// The non-fungible assets. - // TODO: Consider BTreeMap> - // or even BTreeMap> - pub non_fungible: BTreeSet<(AssetId, AssetInstance)>, -} - -impl From for AssetsInHolding { - fn from(asset: Asset) -> AssetsInHolding { - let mut result = Self::default(); - result.subsume(asset); - result - } -} - -impl From> for AssetsInHolding { - fn from(assets: Vec) -> AssetsInHolding { - let mut result = Self::default(); - for asset in assets.into_iter() { - result.subsume(asset) - } - result - } -} - -impl From for AssetsInHolding { - fn from(assets: Assets) -> AssetsInHolding { - assets.into_inner().into() - } -} - -impl From for Vec { - fn from(a: AssetsInHolding) -> Self { - a.into_assets_iter().collect() - } -} - -impl From for Assets { - fn from(a: AssetsInHolding) -> Self { - a.into_assets_iter().collect::>().into() - } -} - -/// 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(Asset), -} - -impl AssetsInHolding { - /// New value, containing no assets. - pub fn new() -> Self { - Self::default() - } - - /// Total number of distinct assets. - pub fn len(&self) -> usize { - self.fungible.len() + self.non_fungible.len() - } - - /// Returns `true` if `self` contains no assets. - pub fn is_empty(&self) -> bool { - self.fungible.is_empty() && self.non_fungible.is_empty() - } - - /// A borrowing iterator over the fungible assets. - pub fn fungible_assets_iter(&self) -> impl Iterator + '_ { - self.fungible.iter().map(|(id, &amount)| Asset { fun: Fungible(amount), id: id.clone() }) - } - - /// A borrowing iterator over the non-fungible assets. - pub fn non_fungible_assets_iter(&self) -> impl Iterator + '_ { - self.non_fungible.iter().map(|(id, instance)| Asset { fun: NonFungible(*instance), id: id.clone() }) - } - - /// A consuming iterator over all assets. - pub fn into_assets_iter(self) -> impl Iterator { - self.fungible - .into_iter() - .map(|(id, amount)| Asset { fun: Fungible(amount), id }) - .chain(self.non_fungible.into_iter().map(|(id, instance)| Asset { fun: NonFungible(instance), id })) - } - - /// A borrowing iterator over all assets. - pub fn assets_iter(&self) -> impl Iterator + '_ { - self.fungible_assets_iter().chain(self.non_fungible_assets_iter()) - } - - /// Mutate `self` to contain all given `assets`, saturating if necessary. - /// - /// NOTE: [`AssetsInHolding`] are always sorted, allowing us to optimize this function from - /// `O(n^2)` to `O(n)`. - pub fn subsume_assets(&mut self, mut assets: AssetsInHolding) { - 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 `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).saturating_accrue(*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. - /// - /// Wildcard values of `asset` do nothing. - pub fn subsume(&mut self, asset: Asset) { - 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)); - }, - } - } - - /// Swaps two mutable AssetsInHolding, without deinitializing either one. - pub fn swapped(&mut self, mut with: AssetsInHolding) -> Self { - mem::swap(&mut *self, &mut with); - with - } - - /// Alter any concretely identified assets by prepending the given `Location`. - /// - /// WARNING: For now we consider this infallible and swallow any errors. It is thus the caller's - /// responsibility to ensure that any internal asset IDs are able to be prepended without - /// overflow. - pub fn prepend_location(&mut self, prepend: &Location) { - let mut fungible = Default::default(); - mem::swap(&mut self.fungible, &mut fungible); - self.fungible = fungible - .into_iter() - .map(|(mut id, amount)| { - let _ = id.prepend_with(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_with(prepend); - (class, inst) - }) - .collect(); - } - - /// Mutate the assets to be interpreted as the same assets from the perspective of a `target` - /// chain. The local chain's `context` is provided. - /// - /// Any assets which were unable to be reanchored are introduced into `failed_bin`. - pub fn reanchor(&mut self, target: &Location, context: &InteriorLocation, mut maybe_failed_bin: Option<&mut Self>) { - let mut fungible = Default::default(); - mem::swap(&mut self.fungible, &mut fungible); - self.fungible = fungible - .into_iter() - .filter_map(|(mut id, amount)| match id.reanchor(target, context) { - Ok(()) => Some((id, amount)), - Err(()) => { - maybe_failed_bin.as_mut().map(|f| f.fungible.insert(id, amount)); - None - }, - }) - .collect(); - let mut non_fungible = Default::default(); - mem::swap(&mut self.non_fungible, &mut non_fungible); - self.non_fungible = non_fungible - .into_iter() - .filter_map(|(mut class, inst)| match class.reanchor(target, context) { - Ok(()) => Some((class, inst)), - Err(()) => { - maybe_failed_bin.as_mut().map(|f| f.non_fungible.insert((class, inst))); - None - }, - }) - .collect(); - } - - /// Returns `true` if `asset` is contained within `self`. - pub fn contains_asset(&self, asset: &Asset) -> bool { - match asset { - Asset { fun: Fungible(amount), id } => self.fungible.get(id).map_or(false, |a| a >= amount), - Asset { fun: NonFungible(instance), id } => self.non_fungible.contains(&(id.clone(), *instance)), - } - } - - /// Returns `true` if all `assets` are contained within `self`. - pub fn contains_assets(&self, assets: &Assets) -> bool { - assets.inner().iter().all(|a| self.contains_asset(a)) - } - - /// Returns `true` if all `assets` are contained within `self`. - pub fn contains(&self, assets: &AssetsInHolding) -> bool { - assets.fungible.iter().all(|(k, v)| self.fungible.get(k).map_or(false, |a| a >= v)) && - self.non_fungible.is_superset(&assets.non_fungible) - } - - /// 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. - pub fn ensure_contains(&self, assets: &Assets) -> Result<(), TakeError> { - for asset in assets.inner().iter() { - match asset { - Asset { fun: Fungible(amount), id } => - if self.fungible.get(id).map_or(true, |a| a < amount) { - return Err(TakeError::AssetUnderflow((id.clone(), *amount).into())) - }, - Asset { fun: NonFungible(instance), id } => { - let id_instance = (id.clone(), *instance); - if !self.non_fungible.contains(&id_instance) { - return Err(TakeError::AssetUnderflow(id_instance.into())) - } - }, - } - } - return Ok(()) - } - - /// Mutates `self` to its original value less `mask` and returns assets that were removed. - /// - /// 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 `mask` references a definite asset which `self` - /// does not contain then an error is returned. - /// - /// The number of unique assets which are removed will respect the `count` parameter in the - /// counted wildcard variants. - /// - /// 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: AssetFilter, saturate: bool) -> Result { - let mut taken = AssetsInHolding::new(); - let maybe_limit = mask.limit().map(|x| x as usize); - match mask { - // TODO: Counted variants where we define `limit`. - AssetFilter::Wild(All) | AssetFilter::Wild(AllCounted(_)) => { - if maybe_limit.map_or(true, |l| self.len() <= l) { - return Ok(self.swapped(AssetsInHolding::new())) - } else { - let fungible = mem::replace(&mut self.fungible, Default::default()); - fungible.into_iter().for_each(|(c, amount)| { - if maybe_limit.map_or(true, |l| taken.len() < l) { - 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 maybe_limit.map_or(true, |l| taken.len() < l) { - taken.non_fungible.insert((c, instance)); - } else { - self.non_fungible.insert((c, instance)); - } - }); - } - }, - AssetFilter::Wild(AllOfCounted { fun: WildFungible, id, .. }) | - AssetFilter::Wild(AllOf { fun: WildFungible, id }) => - if maybe_limit.map_or(true, |l| l >= 1) { - if let Some((id, amount)) = self.fungible.remove_entry(&id) { - taken.fungible.insert(id, amount); - } - }, - AssetFilter::Wild(AllOfCounted { fun: WildNonFungible, id, .. }) | - AssetFilter::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 && maybe_limit.map_or(true, |l| taken.len() < l) { - taken.non_fungible.insert((c, instance)); - } else { - self.non_fungible.insert((c, instance)); - } - }); - }, - AssetFilter::Definite(assets) => { - if !saturate { - self.ensure_contains(&assets)?; - } - for asset in assets.into_inner().into_iter() { - match asset { - Asset { 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) - }, - None => (false, 0), - }; - if remove { - self.fungible.remove(&id); - } - if amount > 0 { - taken.subsume(Asset::from((id, amount)).into()); - } - }, - Asset { fun: NonFungible(instance), id } => { - let id_instance = (id, instance); - if self.non_fungible.remove(&id_instance) { - taken.subsume(id_instance.into()) - } - }, - } - } - }, - } - Ok(taken) - } - - /// 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 saturating_take(&mut self, asset: AssetFilter) -> AssetsInHolding { - self.general_take(asset, true).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 `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: AssetFilter) -> Result { - 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: Asset) -> 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) - }, - } - } - - /// Return the assets in `self`, but (asset-wise) of no greater value than `mask`. - /// - /// The number of unique assets which are returned will respect the `count` parameter in the - /// counted wildcard variants of `mask`. - /// - /// Example: - /// - /// ``` - /// use staging_xcm_executor::AssetsInHolding; - /// use xcm::latest::prelude::*; - /// let assets_i_have: AssetsInHolding = vec![ (Here, 100).into(), (Junctions::from([GeneralIndex(0)]), 100).into() ].into(); - /// let assets_they_want: AssetFilter = vec![ (Here, 200).into(), (Junctions::from([GeneralIndex(0)]), 50).into() ].into(); - /// - /// let assets_we_can_trade: AssetsInHolding = assets_i_have.min(&assets_they_want); - /// assert_eq!(assets_we_can_trade.into_assets_iter().collect::>(), vec![ - /// (Here, 100).into(), (Junctions::from([GeneralIndex(0)]), 50).into(), - /// ]); - /// ``` - pub fn min(&self, mask: &AssetFilter) -> AssetsInHolding { - let mut masked = AssetsInHolding::new(); - let maybe_limit = mask.limit().map(|x| x as usize); - if maybe_limit.map_or(false, |l| l == 0) { - return masked - } - match mask { - AssetFilter::Wild(All) | AssetFilter::Wild(AllCounted(_)) => { - if maybe_limit.map_or(true, |l| self.len() <= l) { - return self.clone() - } else { - for (c, &amount) in self.fungible.iter() { - masked.fungible.insert(c.clone(), amount); - if maybe_limit.map_or(false, |l| masked.len() >= l) { - return masked - } - } - for (c, instance) in self.non_fungible.iter() { - masked.non_fungible.insert((c.clone(), *instance)); - if maybe_limit.map_or(false, |l| masked.len() >= l) { - return masked - } - } - } - }, - AssetFilter::Wild(AllOfCounted { fun: WildFungible, id, .. }) | - AssetFilter::Wild(AllOf { fun: WildFungible, id }) => - if let Some(&amount) = self.fungible.get(&id) { - masked.fungible.insert(id.clone(), amount); - }, - AssetFilter::Wild(AllOfCounted { fun: WildNonFungible, id, .. }) | - AssetFilter::Wild(AllOf { fun: WildNonFungible, id }) => - for (c, instance) in self.non_fungible.iter() { - if c == id { - masked.non_fungible.insert((c.clone(), *instance)); - if maybe_limit.map_or(false, |l| masked.len() >= l) { - return masked - } - } - }, - AssetFilter::Definite(assets) => - for asset in assets.inner().iter() { - match asset { - Asset { fun: Fungible(amount), id } => - if let Some(m) = self.fungible.get(id) { - masked.subsume((id.clone(), Fungible(*amount.min(m))).into()); - }, - Asset { fun: NonFungible(instance), id } => { - let id_instance = (id.clone(), *instance); - if self.non_fungible.contains(&id_instance) { - masked.subsume(id_instance.into()); - } - }, - } - }, - } - masked - } -} - -#[cfg(test)] -mod tests { - use super::*; - use xcm::latest::prelude::*; - #[allow(non_snake_case)] - /// Concrete fungible constructor - fn CF(amount: u128) -> Asset { - (Here, amount).into() - } - #[allow(non_snake_case)] - /// Concrete non-fungible constructor - fn CNF(instance_id: u8) -> Asset { - (Here, [instance_id; 4]).into() - } - - fn test_assets() -> AssetsInHolding { - let mut assets = AssetsInHolding::new(); - assets.subsume(CF(300)); - assets.subsume(CNF(40)); - assets - } - - #[test] - fn subsume_assets_works() { - let t1 = test_assets(); - let mut t2 = AssetsInHolding::new(); - t2.subsume(CF(300)); - t2.subsume(CNF(50)); - 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(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(CNF(41)).unwrap_err(); - let t = t.checked_sub(CNF(40)).unwrap(); - let t = t.checked_sub(CNF(40)).unwrap_err(); - assert_eq!(t, AssetsInHolding::new()); - } - - #[test] - fn into_assets_iter_works() { - let assets = test_assets(); - let mut iter = assets.into_assets_iter(); - // Order defined by implementation: CF, CNF - assert_eq!(Some(CF(300)), iter.next()); - assert_eq!(Some(CNF(40)), iter.next()); - assert_eq!(None, iter.next()); - } - - #[test] - fn assets_into_works() { - let mut assets_vec: Vec = Vec::new(); - assets_vec.push(CF(300)); - assets_vec.push(CNF(40)); - // Push same group of tokens again - assets_vec.push(CF(300)); - assets_vec.push(CNF(40)); - - let assets: AssetsInHolding = assets_vec.into(); - let mut iter = assets.into_assets_iter(); - // Fungibles add - assert_eq!(Some(CF(600)), iter.next()); - // Non-fungibles collapse - assert_eq!(Some(CNF(40)), iter.next()); - assert_eq!(None, iter.next()); - } - - #[test] - fn min_all_and_none_works() { - let assets = test_assets(); - let none = Assets::new().into(); - let all = All.into(); - - let none_min = assets.min(&none); - assert_eq!(None, none_min.assets_iter().next()); - let all_min = assets.min(&all); - assert!(all_min.assets_iter().eq(assets.assets_iter())); - } - - #[test] - fn min_counted_works() { - let mut assets = AssetsInHolding::new(); - assets.subsume(CNF(40)); - assets.subsume(CF(3000)); - assets.subsume(CNF(80)); - let all = WildAsset::AllCounted(6).into(); - - let all = assets.min(&all); - let all = all.assets_iter().collect::>(); - assert_eq!(all, vec![CF(3000), CNF(40), CNF(80)]); - } - - #[test] - fn min_all_concrete_works() { - let assets = test_assets(); - let fungible = Wild((Here, WildFungible).into()); - let non_fungible = Wild((Here, WildNonFungible).into()); - - let fungible = assets.min(&fungible); - let fungible = fungible.assets_iter().collect::>(); - 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(40)]); - } - - #[test] - fn min_basic_works() { - let assets1 = test_assets(); - - let mut assets2 = AssetsInHolding::new(); - // This is more then 300, so it should stay at 300 - assets2.subsume(CF(600)); - // This asset should be included - assets2.subsume(CNF(40)); - let assets2: Assets = 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), CNF(40)]); - } - - #[test] - fn saturating_take_all_and_none_works() { - let mut assets = test_assets(); - - let taken_none = assets.saturating_take(vec![].into()); - assert_eq!(None, taken_none.assets_iter().next()); - 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_concrete_works() { - let mut assets = test_assets(); - 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::>(); - 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(40)]); - } - - #[test] - fn saturating_take_basic_works() { - let mut assets1 = test_assets(); - - let mut assets2 = AssetsInHolding::new(); - // This is more then 300, so it takes everything - assets2.subsume(CF(600)); - // This asset should be taken - assets2.subsume(CNF(40)); - let assets2: Assets = assets2.into(); - - let taken = assets1.saturating_take(assets2.into()); - let taken = taken.into_assets_iter().collect::>(); - assert_eq!(taken, vec![CF(300), CNF(40)]); - } - - #[test] - fn try_take_all_counted_works() { - let mut assets = AssetsInHolding::new(); - assets.subsume(CNF(40)); - assets.subsume(CF(3000)); - assets.subsume(CNF(80)); - let all = assets.try_take(WildAsset::AllCounted(6).into()).unwrap(); - assert_eq!(Assets::from(all).inner(), &vec![CF(3000), CNF(40), CNF(80)]); - } - - #[test] - fn try_take_fungibles_counted_works() { - let mut assets = AssetsInHolding::new(); - assets.subsume(CNF(40)); - assets.subsume(CF(3000)); - assets.subsume(CNF(80)); - assert_eq!(Assets::from(assets).inner(), &vec![CF(3000), CNF(40), CNF(80),]); - } - - #[test] - fn try_take_non_fungibles_counted_works() { - let mut assets = AssetsInHolding::new(); - assets.subsume(CNF(40)); - assets.subsume(CF(3000)); - assets.subsume(CNF(80)); - assert_eq!(Assets::from(assets).inner(), &vec![CF(3000), CNF(40), CNF(80)]); - } -} diff --git a/debug-executor/src/config.rs b/debug-executor/src/config.rs deleted file mode 100644 index 30d30cc7f..000000000 --- a/debug-executor/src/config.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use crate::traits::{ - AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, DropAssets, ExportXcm, FeeManager, - HandleHrmpChannelAccepted, HandleHrmpChannelClosing, HandleHrmpNewChannelOpenRequest, OnResponse, - ProcessTransaction, RecordXcm, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, -}; -use frame_support::{ - dispatch::{GetDispatchInfo, Parameter, PostDispatchInfo}, - traits::{Contains, ContainsPair, Get, PalletsInfoAccess}, -}; -use sp_runtime::traits::Dispatchable; -use xcm::prelude::*; - -/// The trait to parameterize the `XcmExecutor`. -pub trait Config { - /// The outer call dispatch type. - type RuntimeCall: Parameter + Dispatchable + GetDispatchInfo; - - /// How to send an onward XCM message. - type XcmSender: SendXcm; - - /// How to withdraw and deposit an asset. - type AssetTransactor: TransactAsset; - - /// How to get a call origin from a `OriginKind` value. - type OriginConverter: ConvertOrigin<::RuntimeOrigin>; - - /// Combinations of (Asset, Location) pairs which we trust as reserves. - type IsReserve: ContainsPair; - - /// Combinations of (Asset, Location) pairs which we trust as teleporters. - type IsTeleporter: ContainsPair; - - /// A list of (Origin, Target) pairs allowing a given Origin to be substituted with its - /// corresponding Target pair. - type Aliasers: ContainsPair; - - /// This chain's Universal Location. - type UniversalLocation: Get; - - /// Whether we should execute the given XCM at all. - type Barrier: ShouldExecute; - - /// The means of determining an XCM message's weight. - type Weigher: WeightBounds; - - /// The means of purchasing weight credit for XCM execution. - type Trader: WeightTrader; - - /// What to do when a response of a query is found. - type ResponseHandler: OnResponse; - - /// The general asset trap - handler for when assets are left in the Holding Register at the - /// end of execution. - type AssetTrap: DropAssets; - - /// Handler for asset locking. - type AssetLocker: AssetLock; - - /// Handler for exchanging assets. - type AssetExchanger: AssetExchange; - - /// The handler for when there is an instruction to claim assets. - type AssetClaims: ClaimAssets; - - /// How we handle version subscription requests. - type SubscriptionService: VersionChangeNotifier; - - /// Information on all pallets. - type PalletInstancesInfo: PalletsInfoAccess; - - /// The maximum number of assets we target to have in the Holding Register at any one time. - /// - /// NOTE: In the worse case, the Holding Register may contain up to twice as many assets as this - /// and any benchmarks should take that into account. - type MaxAssetsIntoHolding: Get; - - /// Configure the fees. - type FeeManager: FeeManager; - - /// The method of exporting a message. - type MessageExporter: ExportXcm; - - /// The origin locations and specific universal junctions to which they are allowed to elevate - /// themselves. - type UniversalAliases: Contains<(Location, Junction)>; - - /// The call dispatcher used by XCM. - /// - /// XCM will use this to dispatch any calls. When no special call dispatcher is required, - /// this can be set to the same type as `Self::Call`. - type CallDispatcher: CallDispatcher; - - /// The safe call filter for `Transact`. - /// - /// Use this type to explicitly whitelist calls that cannot undergo recursion. This is a - /// temporary measure until we properly account for proof size weights for XCM instructions. - type SafeCallFilter: Contains; - - /// Transactional processor for XCM instructions. - type TransactionalProcessor: ProcessTransaction; - - /// Allows optional logic execution for the `HrmpNewChannelOpenRequest` XCM notification. - type HrmpNewChannelOpenRequestHandler: HandleHrmpNewChannelOpenRequest; - /// Allows optional logic execution for the `HrmpChannelAccepted` XCM notification. - type HrmpChannelAcceptedHandler: HandleHrmpChannelAccepted; - /// Allows optional logic execution for the `HrmpChannelClosing` XCM notification. - type HrmpChannelClosingHandler: HandleHrmpChannelClosing; - /// Allows recording the last executed XCM (used by dry-run runtime APIs). - type XcmRecorder: RecordXcm; -} diff --git a/debug-executor/src/lib.rs b/debug-executor/src/lib.rs deleted file mode 100644 index 0330f843d..000000000 --- a/debug-executor/src/lib.rs +++ /dev/null @@ -1,1188 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::{Decode, Encode}; -use frame_support::{ - dispatch::GetDispatchInfo, - ensure, - traits::{Contains, ContainsPair, Defensive, Get, PalletsInfoAccess}, -}; -use sp_core::defer; -use sp_io::hashing::blake2_128; -use sp_std::{fmt::Debug, marker::PhantomData, prelude::*}; -use sp_weights::Weight; -use xcm::latest::prelude::*; - -pub mod traits; -use traits::{ - validate_export, AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin, DropAssets, Enact, - ExportXcm, FeeManager, FeeReason, HandleHrmpChannelAccepted, HandleHrmpChannelClosing, - HandleHrmpNewChannelOpenRequest, OnResponse, ProcessTransaction, Properties, ShouldExecute, TransactAsset, - VersionChangeNotifier, WeightBounds, WeightTrader, XcmAssetTransfers, -}; - -pub use traits::RecordXcm; - -mod assets; -pub use assets::AssetsInHolding; -mod config; -pub use config::Config; - -/// A struct to specify how fees are being paid. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct FeesMode { - /// If true, then the fee assets are taken directly from the origin's on-chain account, - /// otherwise the fee assets are taken from the holding register. - /// - /// Defaults to false. - pub jit_withdraw: bool, -} - -const RECURSION_LIMIT: u8 = 10; - -environmental::environmental!(recursion_count: u8); - -/// The XCM executor. -pub struct XcmExecutor { - holding: AssetsInHolding, - holding_limit: usize, - context: XcmContext, - original_origin: Location, - trader: Config::Trader, - /// The most recent error result and instruction index into the fragment in which it occurred, - /// if any. - error: Option<(u32, XcmError)>, - /// The surplus weight, defined as the amount by which `max_weight` is - /// an over-estimate of the actual weight consumed. We do it this way to avoid needing the - /// execution engine to keep track of all instructions' weights (it only needs to care about - /// the weight of dynamically determined instructions such as `Transact`). - total_surplus: Weight, - total_refunded: Weight, - error_handler: Xcm, - error_handler_weight: Weight, - appendix: Xcm, - appendix_weight: Weight, - transact_status: MaybeErrorCode, - fees_mode: FeesMode, - _config: PhantomData, -} - -#[cfg(feature = "runtime-benchmarks")] -impl XcmExecutor { - pub fn holding(&self) -> &AssetsInHolding { - &self.holding - } - - pub fn set_holding(&mut self, v: AssetsInHolding) { - self.holding = v - } - - pub fn holding_limit(&self) -> &usize { - &self.holding_limit - } - - pub fn set_holding_limit(&mut self, v: usize) { - self.holding_limit = v - } - - pub fn origin(&self) -> &Option { - &self.context.origin - } - - pub fn set_origin(&mut self, v: Option) { - self.context.origin = v - } - - pub fn original_origin(&self) -> &Location { - &self.original_origin - } - - pub fn set_original_origin(&mut self, v: Location) { - self.original_origin = v - } - - pub fn trader(&self) -> &Config::Trader { - &self.trader - } - - pub fn set_trader(&mut self, v: Config::Trader) { - self.trader = v - } - - pub fn error(&self) -> &Option<(u32, XcmError)> { - &self.error - } - - pub fn set_error(&mut self, v: Option<(u32, XcmError)>) { - self.error = v - } - - pub fn total_surplus(&self) -> &Weight { - &self.total_surplus - } - - pub fn set_total_surplus(&mut self, v: Weight) { - self.total_surplus = v - } - - pub fn total_refunded(&self) -> &Weight { - &self.total_refunded - } - - pub fn set_total_refunded(&mut self, v: Weight) { - self.total_refunded = v - } - - pub fn error_handler(&self) -> &Xcm { - &self.error_handler - } - - pub fn set_error_handler(&mut self, v: Xcm) { - self.error_handler = v - } - - pub fn error_handler_weight(&self) -> &Weight { - &self.error_handler_weight - } - - pub fn set_error_handler_weight(&mut self, v: Weight) { - self.error_handler_weight = v - } - - pub fn appendix(&self) -> &Xcm { - &self.appendix - } - - pub fn set_appendix(&mut self, v: Xcm) { - self.appendix = v - } - - pub fn appendix_weight(&self) -> &Weight { - &self.appendix_weight - } - - pub fn set_appendix_weight(&mut self, v: Weight) { - self.appendix_weight = v - } - - pub fn transact_status(&self) -> &MaybeErrorCode { - &self.transact_status - } - - pub fn set_transact_status(&mut self, v: MaybeErrorCode) { - self.transact_status = v - } - - pub fn fees_mode(&self) -> &FeesMode { - &self.fees_mode - } - - pub fn set_fees_mode(&mut self, v: FeesMode) { - self.fees_mode = v - } - - pub fn topic(&self) -> &Option<[u8; 32]> { - &self.context.topic - } - - pub fn set_topic(&mut self, v: Option<[u8; 32]>) { - self.context.topic = v; - } -} - -pub struct WeighedMessage(Weight, Xcm); -impl PreparedMessage for WeighedMessage { - fn weight_of(&self) -> Weight { - self.0 - } -} - -#[cfg(any(test, feature = "std"))] -impl WeighedMessage { - pub fn new(weight: Weight, message: Xcm) -> Self { - Self(weight, message) - } -} - -impl ExecuteXcm for XcmExecutor { - type Prepared = WeighedMessage; - - fn prepare(mut message: Xcm) -> Result> { - match Config::Weigher::weight(&mut message) { - Ok(weight) => Ok(WeighedMessage(weight, message)), - Err(_) => Err(message), - } - } - - fn execute( - origin: impl Into, - WeighedMessage(xcm_weight, mut message): WeighedMessage, - id: &mut XcmHash, - weight_credit: Weight, - ) -> Outcome { - let origin = origin.into(); - log::trace!( - target: "xcm::execute", - "origin: {origin:?}, message: {message:?}, weight_credit: {weight_credit:?}", - ); - let mut properties = Properties { weight_credit, message_id: None }; - - // We only want to record under certain conditions (mainly only during dry-running), - // so as to not degrade regular performance. - if Config::XcmRecorder::should_record() { - Config::XcmRecorder::record(message.clone().into()); - } - - if let Err(e) = Config::Barrier::should_execute(&origin, message.inner_mut(), xcm_weight, &mut properties) { - log::trace!( - target: "xcm::execute", - "Barrier blocked execution! Error: {e:?}. \ - (origin: {origin:?}, message: {message:?}, properties: {properties:?})", - ); - return Outcome::Error { error: XcmError::Barrier } - } - - *id = properties.message_id.unwrap_or(*id); - - let mut vm = Self::new(origin, *id); - - while !message.0.is_empty() { - let result = vm.process(message); - log::trace!(target: "xcm::execute", "result: {result:?}"); - message = if let Err(error) = result { - vm.total_surplus.saturating_accrue(error.weight); - vm.error = Some((error.index, error.xcm_error)); - vm.take_error_handler().or_else(|| vm.take_appendix()) - } else { - vm.drop_error_handler(); - vm.take_appendix() - } - } - - vm.post_process(xcm_weight) - } - - fn charge_fees(origin: impl Into, fees: Assets) -> XcmResult { - let origin = origin.into(); - if !Config::FeeManager::is_waived(Some(&origin), FeeReason::ChargeFees) { - for asset in fees.inner() { - Config::AssetTransactor::withdraw_asset(&asset, &origin, None)?; - } - Config::FeeManager::handle_fee(fees, None, FeeReason::ChargeFees); - } - Ok(()) - } -} - -impl XcmAssetTransfers for XcmExecutor { - type AssetTransactor = Config::AssetTransactor; - type IsReserve = Config::IsReserve; - type IsTeleporter = Config::IsTeleporter; -} - -#[derive(Debug)] -pub struct ExecutorError { - pub index: u32, - pub xcm_error: XcmError, - pub weight: Weight, -} - -#[cfg(feature = "runtime-benchmarks")] -impl From for frame_benchmarking::BenchmarkError { - fn from(error: ExecutorError) -> Self { - log::error!("XCM ERROR >> Index: {:?}, Error: {:?}, Weight: {:?}", error.index, error.xcm_error, error.weight); - Self::Stop("xcm executor error: see error logs") - } -} - -impl XcmExecutor { - pub fn new(origin: impl Into, message_id: XcmHash) -> Self { - let origin = origin.into(); - Self { - holding: AssetsInHolding::new(), - holding_limit: Config::MaxAssetsIntoHolding::get() as usize, - context: XcmContext { origin: Some(origin.clone()), message_id, topic: None }, - original_origin: origin, - trader: Config::Trader::new(), - error: None, - total_surplus: Weight::zero(), - total_refunded: Weight::zero(), - error_handler: Xcm(vec![]), - error_handler_weight: Weight::zero(), - appendix: Xcm(vec![]), - appendix_weight: Weight::zero(), - transact_status: Default::default(), - fees_mode: FeesMode { jit_withdraw: false }, - _config: PhantomData, - } - } - - /// Execute any final operations after having executed the XCM message. - /// This includes refunding surplus weight, trapping extra holding funds, and returning any - /// errors during execution. - pub fn post_process(mut self, xcm_weight: Weight) -> Outcome { - // We silently drop any error from our attempt to refund the surplus as it's a charitable - // thing so best-effort is all we will do. - let _ = self.refund_surplus(); - drop(self.trader); - - let mut weight_used = xcm_weight.saturating_sub(self.total_surplus); - - if !self.holding.is_empty() { - log::trace!( - target: "xcm::post_process", - "Trapping assets in holding register: {:?}, context: {:?} (original_origin: {:?})", - self.holding, self.context, self.original_origin, - ); - let effective_origin = self.context.origin.as_ref().unwrap_or(&self.original_origin); - let trap_weight = Config::AssetTrap::drop_assets(effective_origin, self.holding, &self.context); - weight_used.saturating_accrue(trap_weight); - }; - - match self.error { - None => Outcome::Complete { used: weight_used }, - // TODO: #2841 #REALWEIGHT We should deduct the cost of any instructions following - // the error which didn't end up being executed. - Some((_i, e)) => { - log::trace!(target: "xcm::post_process", "Execution errored at {:?}: {:?} (original_origin: {:?})", _i, e, self.original_origin); - Outcome::Incomplete { used: weight_used, error: e } - }, - } - } - - fn origin_ref(&self) -> Option<&Location> { - self.context.origin.as_ref() - } - - fn cloned_origin(&self) -> Option { - self.context.origin.clone() - } - - /// Send an XCM, charging fees from Holding as needed. - fn send(&mut self, dest: Location, msg: Xcm<()>, reason: FeeReason) -> Result { - log::trace!( - target: "xcm::send", "Sending msg: {msg:?}, to destination: {dest:?}, (reason: {reason:?})" - ); - let (ticket, fee) = validate_send::(dest, msg)?; - self.take_fee(fee, reason)?; - Config::XcmSender::deliver(ticket).map_err(Into::into) - } - - /// Remove the registered error handler and return it. Do not refund its weight. - fn take_error_handler(&mut self) -> Xcm { - let mut r = Xcm::(vec![]); - sp_std::mem::swap(&mut self.error_handler, &mut r); - self.error_handler_weight = Weight::zero(); - r - } - - /// Drop the registered error handler and refund its weight. - fn drop_error_handler(&mut self) { - self.error_handler = Xcm::(vec![]); - self.total_surplus.saturating_accrue(self.error_handler_weight); - self.error_handler_weight = Weight::zero(); - } - - /// Remove the registered appendix and return it. - fn take_appendix(&mut self) -> Xcm { - let mut r = Xcm::(vec![]); - sp_std::mem::swap(&mut self.appendix, &mut r); - self.appendix_weight = Weight::zero(); - r - } - - fn ensure_can_subsume_assets(&self, assets_length: usize) -> Result<(), XcmError> { - // worst-case, holding.len becomes 2 * holding_limit. - // this guarantees that if holding.len() == holding_limit and you have more than - // `holding_limit` items (which has a best case outcome of holding.len() == holding_limit), - // then the operation is guaranteed to succeed. - let worst_case_holding_len = self.holding.len() + assets_length; - log::trace!(target: "xcm::ensure_can_subsume_assets", "worst_case_holding_len: {:?}, holding_limit: {:?}", worst_case_holding_len, self.holding_limit); - ensure!(worst_case_holding_len <= self.holding_limit * 2, XcmError::HoldingWouldOverflow); - Ok(()) - } - - /// Refund any unused weight. - fn refund_surplus(&mut self) -> Result<(), XcmError> { - let current_surplus = self.total_surplus.saturating_sub(self.total_refunded); - log::trace!( - target: "xcm::refund_surplus", - "total_surplus: {:?}, total_refunded: {:?}, current_surplus: {:?}", - self.total_surplus, - self.total_refunded, - current_surplus, - ); - if current_surplus.any_gt(Weight::zero()) { - if let Some(w) = self.trader.refund_weight(current_surplus, &self.context) { - if !self.holding.contains_asset(&(w.id.clone(), 1).into()) && self.ensure_can_subsume_assets(1).is_err() - { - let _ = self - .trader - .buy_weight(current_surplus, w.into(), &self.context) - .defensive_proof("refund_weight returned an asset capable of buying weight; qed"); - log::error!( - target: "xcm::refund_surplus", - "error: HoldingWouldOverflow", - ); - return Err(XcmError::HoldingWouldOverflow) - } - self.total_refunded.saturating_accrue(current_surplus); - self.holding.subsume_assets(w.into()); - } - } - log::trace!( - target: "xcm::refund_surplus", - "total_refunded: {:?}", - self.total_refunded, - ); - Ok(()) - } - - fn take_fee(&mut self, fee: Assets, reason: FeeReason) -> XcmResult { - if Config::FeeManager::is_waived(self.origin_ref(), reason.clone()) { - return Ok(()) - } - log::trace!( - target: "xcm::fees", - "taking fee: {:?} from origin_ref: {:?} in fees_mode: {:?} for a reason: {:?}", - fee, - self.origin_ref(), - self.fees_mode, - reason, - ); - let paid = if self.fees_mode.jit_withdraw { - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - for asset in fee.inner() { - Config::AssetTransactor::withdraw_asset(&asset, origin, Some(&self.context))?; - } - fee - } else { - self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?.into() - }; - Config::FeeManager::handle_fee(paid, Some(&self.context), reason); - Ok(()) - } - - /// Calculates what `local_querier` would be from the perspective of `destination`. - fn to_querier(local_querier: Option, destination: &Location) -> Result, XcmError> { - Ok(match local_querier { - None => None, - Some(q) => Some( - q.reanchored(&destination, &Config::UniversalLocation::get()).map_err(|_| XcmError::ReanchorFailed)?, - ), - }) - } - - /// Send a bare `QueryResponse` message containing `response` informed by the given `info`. - /// - /// The `local_querier` argument is the querier (if any) specified from the *local* perspective. - fn respond( - &mut self, - local_querier: Option, - response: Response, - info: QueryResponseInfo, - fee_reason: FeeReason, - ) -> Result { - let querier = Self::to_querier(local_querier, &info.destination)?; - let QueryResponseInfo { destination, query_id, max_weight } = info; - let instruction = QueryResponse { query_id, response, max_weight, querier }; - let message = Xcm(vec![instruction]); - self.send(destination, message, fee_reason) - } - - fn try_reanchor( - reanchorable: T, - destination: &Location, - ) -> Result<(T, InteriorLocation), XcmError> { - let reanchor_context = Config::UniversalLocation::get(); - let reanchored = reanchorable.reanchored(&destination, &reanchor_context).map_err(|error| { - log::error!(target: "xcm::reanchor", "Failed reanchoring with error {error:?}"); - XcmError::ReanchorFailed - })?; - Ok((reanchored, reanchor_context)) - } - - /// NOTE: Any assets which were unable to be reanchored are introduced into `failed_bin`. - fn reanchored( - mut assets: AssetsInHolding, - dest: &Location, - maybe_failed_bin: Option<&mut AssetsInHolding>, - ) -> Assets { - let reanchor_context = Config::UniversalLocation::get(); - assets.reanchor(dest, &reanchor_context, maybe_failed_bin); - assets.into_assets_iter().collect::>().into() - } - - #[cfg(feature = "runtime-benchmarks")] - pub fn bench_process(&mut self, xcm: Xcm) -> Result<(), ExecutorError> { - self.process(xcm) - } - - fn process(&mut self, xcm: Xcm) -> Result<(), ExecutorError> { - log::trace!( - target: "xcm::process", - "origin: {:?}, total_surplus/refunded: {:?}/{:?}, error_handler_weight: {:?}", - self.origin_ref(), - self.total_surplus, - self.total_refunded, - self.error_handler_weight, - ); - let mut result = Ok(()); - for (i, instr) in xcm.0.into_iter().enumerate() { - match &mut result { - r @ Ok(()) => { - // Initialize the recursion count only the first time we hit this code in our - // potential recursive execution. - let inst_res = recursion_count::using_once(&mut 1, || { - recursion_count::with(|count| { - if *count > RECURSION_LIMIT { - return Err(XcmError::ExceedsStackLimit) - } - *count = count.saturating_add(1); - Ok(()) - }) - // This should always return `Some`, but let's play it safe. - .unwrap_or(Ok(()))?; - - // Ensure that we always decrement the counter whenever we finish processing - // the instruction. - defer! { - recursion_count::with(|count| { - *count = count.saturating_sub(1); - }); - } - - self.process_instruction(instr) - }); - if let Err(e) = inst_res { - log::trace!(target: "xcm::execute", "!!! ERROR: {:?}", e); - *r = Err(ExecutorError { index: i as u32, xcm_error: e, weight: Weight::zero() }); - } - }, - Err(ref mut error) => - if let Ok(x) = Config::Weigher::instr_weight(&instr) { - error.weight.saturating_accrue(x) - }, - } - } - result - } - - /// Process a single XCM instruction, mutating the state of the XCM virtual machine. - fn process_instruction(&mut self, instr: Instruction) -> Result<(), XcmError> { - log::trace!( - target: "xcm::process_instruction", - "=== {:?}", - instr - ); - match instr { - WithdrawAsset(assets) => { - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - self.ensure_can_subsume_assets(assets.len())?; - Config::TransactionalProcessor::process(|| { - // Take `assets` from the origin account (on-chain)... - for asset in assets.inner() { - Config::AssetTransactor::withdraw_asset(asset, origin, Some(&self.context))?; - } - Ok(()) - }) - .and_then(|_| { - // ...and place into holding. - self.holding.subsume_assets(assets.into()); - Ok(()) - }) - }, - ReserveAssetDeposited(assets) => { - // check whether we trust origin to be our reserve location for this asset. - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - self.ensure_can_subsume_assets(assets.len())?; - for asset in assets.inner() { - // Must ensure that we recognise the asset as being managed by the origin. - ensure!(Config::IsReserve::contains(asset, origin), XcmError::UntrustedReserveLocation); - } - self.holding.subsume_assets(assets.into()); - Ok(()) - }, - TransferAsset { assets, beneficiary } => { - Config::TransactionalProcessor::process(|| { - // Take `assets` from the origin account (on-chain) and place into dest account. - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - for asset in assets.inner() { - Config::AssetTransactor::transfer_asset(&asset, origin, &beneficiary, &self.context)?; - } - Ok(()) - }) - }, - TransferReserveAsset { mut assets, dest, xcm } => { - Config::TransactionalProcessor::process(|| { - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - // Take `assets` from the origin account (on-chain) and place into dest account. - for asset in assets.inner() { - Config::AssetTransactor::transfer_asset(asset, origin, &dest, &self.context)?; - } - let reanchor_context = Config::UniversalLocation::get(); - assets.reanchor(&dest, &reanchor_context).map_err(|()| XcmError::LocationFull)?; - let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; - message.extend(xcm.0.into_iter()); - self.send(dest, Xcm(message), FeeReason::TransferReserveAsset)?; - Ok(()) - }) - }, - ReceiveTeleportedAsset(assets) => { - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - self.ensure_can_subsume_assets(assets.len())?; - Config::TransactionalProcessor::process(|| { - // 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::contains(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). - Config::AssetTransactor::can_check_in(origin, asset, &self.context)?; - Config::AssetTransactor::check_in(origin, asset, &self.context); - } - Ok(()) - }) - .and_then(|_| { - self.holding.subsume_assets(assets.into()); - Ok(()) - }) - }, - Transact { origin_kind, require_weight_at_most, mut call } => { - // We assume that the Relay-chain is allowed to use transact on this parachain. - let origin = self.cloned_origin().ok_or_else(|| { - log::trace!( - target: "xcm::process_instruction::transact", - "No origin provided", - ); - - XcmError::BadOrigin - })?; - - // TODO: #2841 #TRANSACTFILTER allow the trait to issue filters for the relay-chain - let message_call = call.take_decoded().map_err(|_| { - log::trace!( - target: "xcm::process_instruction::transact", - "Failed to decode call", - ); - - XcmError::FailedToDecode - })?; - - log::trace!( - target: "xcm::process_instruction::transact", - "Processing call: {message_call:?}", - ); - - if !Config::SafeCallFilter::contains(&message_call) { - log::trace!( - target: "xcm::process_instruction::transact", - "Call filtered by `SafeCallFilter`", - ); - - return Err(XcmError::NoPermission) - } - - let dispatch_origin = - Config::OriginConverter::convert_origin(origin.clone(), origin_kind).map_err(|_| { - log::trace!( - target: "xcm::process_instruction::transact", - "Failed to convert origin {origin:?} and origin kind {origin_kind:?} to a local origin." - ); - - XcmError::BadOrigin - })?; - - log::trace!( - target: "xcm::process_instruction::transact", - "Dispatching with origin: {dispatch_origin:?}", - ); - - let weight = message_call.get_dispatch_info().weight; - - if !weight.all_lte(require_weight_at_most) { - log::trace!( - target: "xcm::process_instruction::transact", - "Max {weight} bigger than require at most {require_weight_at_most}", - ); - - return Err(XcmError::MaxWeightInvalid) - } - - let maybe_actual_weight = match Config::CallDispatcher::dispatch(message_call, dispatch_origin) { - Ok(post_info) => { - log::trace!( - target: "xcm::process_instruction::transact", - "Dispatch successful: {post_info:?}" - ); - self.transact_status = MaybeErrorCode::Success; - post_info.actual_weight - }, - Err(error_and_info) => { - log::trace!( - target: "xcm::process_instruction::transact", - "Dispatch failed {error_and_info:?}" - ); - - self.transact_status = error_and_info.error.encode().into(); - error_and_info.post_info.actual_weight - }, - }; - let actual_weight = maybe_actual_weight.unwrap_or(weight); - let surplus = weight.saturating_sub(actual_weight); - // We assume that the `Config::Weigher` will counts the `require_weight_at_most` - // for the estimate of how much weight this instruction will take. Now that we know - // that it's less, we credit it. - // - // We make the adjustment for the total surplus, which is used eventually - // reported back to the caller and this ensures that they account for the total - // weight consumed correctly (potentially allowing them to do more operations in a - // block than they otherwise would). - self.total_surplus.saturating_accrue(surplus); - Ok(()) - }, - QueryResponse { query_id, response, max_weight, querier } => { - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - Config::ResponseHandler::on_response( - origin, - query_id, - querier.as_ref(), - response, - max_weight, - &self.context, - ); - Ok(()) - }, - DescendOrigin(who) => self - .context - .origin - .as_mut() - .ok_or(XcmError::BadOrigin)? - .append_with(who) - .map_err(|_| XcmError::LocationFull), - ClearOrigin => { - self.context.origin = None; - Ok(()) - }, - ReportError(response_info) => { - // Report the given result by sending a QueryResponse XCM to a previously given - // outcome destination if one was registered. - self.respond( - self.cloned_origin(), - Response::ExecutionResult(self.error), - response_info, - FeeReason::Report, - )?; - Ok(()) - }, - DepositAsset { assets, beneficiary } => { - let old_holding = self.holding.clone(); - let result = Config::TransactionalProcessor::process(|| { - let deposited = self.holding.saturating_take(assets); - for asset in deposited.into_assets_iter() { - Config::AssetTransactor::deposit_asset(&asset, &beneficiary, Some(&self.context))?; - } - Ok(()) - }); - if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { - self.holding = old_holding; - } - result - }, - DepositReserveAsset { assets, dest, xcm } => { - let old_holding = self.holding.clone(); - let result = Config::TransactionalProcessor::process(|| { - // we need to do this take/put cycle to solve wildcards and get exact assets to - // be weighed - let to_weigh = self.holding.saturating_take(assets.clone()); - self.holding.subsume_assets(to_weigh.clone()); - let to_weigh_reanchored = Self::reanchored(to_weigh, &dest, None); - let mut message_to_weigh = vec![ReserveAssetDeposited(to_weigh_reanchored), ClearOrigin]; - message_to_weigh.extend(xcm.0.clone().into_iter()); - let (_, fee) = validate_send::(dest.clone(), Xcm(message_to_weigh))?; - // set aside fee to be charged by XcmSender - let transport_fee = self.holding.saturating_take(fee.into()); - - // now take assets to deposit (excluding transport_fee) - let deposited = self.holding.saturating_take(assets); - for asset in deposited.assets_iter() { - Config::AssetTransactor::deposit_asset(&asset, &dest, Some(&self.context))?; - } - // Note that we pass `None` as `maybe_failed_bin` and drop any assets which - // cannot be reanchored because we have already called `deposit_asset` on all - // assets. - let assets = Self::reanchored(deposited, &dest, None); - let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; - message.extend(xcm.0.into_iter()); - // put back transport_fee in holding register to be charged by XcmSender - self.holding.subsume_assets(transport_fee); - self.send(dest, Xcm(message), FeeReason::DepositReserveAsset)?; - Ok(()) - }); - if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { - self.holding = old_holding; - } - result - }, - InitiateReserveWithdraw { assets, reserve, xcm } => { - let old_holding = self.holding.clone(); - let result = Config::TransactionalProcessor::process(|| { - // Note that here we are able to place any assets which could not be reanchored - // back into Holding. - let assets = - Self::reanchored(self.holding.saturating_take(assets), &reserve, Some(&mut self.holding)); - let mut message = vec![WithdrawAsset(assets), ClearOrigin]; - message.extend(xcm.0.into_iter()); - self.send(reserve, Xcm(message), FeeReason::InitiateReserveWithdraw)?; - Ok(()) - }); - if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { - self.holding = old_holding; - } - result - }, - InitiateTeleport { assets, dest, xcm } => { - let old_holding = self.holding.clone(); - let result = (|| -> Result<(), XcmError> { - // We must do this first in order to resolve wildcards. - let assets = self.holding.saturating_take(assets); - for asset in assets.assets_iter() { - // We should check that the asset can actually be teleported out (for this - // to be in error, there would need to be an accounting violation by - // ourselves, so it's unlikely, but we don't want to allow that kind of bug - // to leak into a trusted chain. - Config::AssetTransactor::can_check_out(&dest, &asset, &self.context)?; - } - // Note that we pass `None` as `maybe_failed_bin` and drop any assets which - // cannot be reanchored because we have already checked all assets out. - let reanchored_assets = Self::reanchored(assets.clone(), &dest, None); - let mut message = vec![ReceiveTeleportedAsset(reanchored_assets), ClearOrigin]; - message.extend(xcm.0.into_iter()); - self.send(dest.clone(), Xcm(message), FeeReason::InitiateTeleport)?; - - for asset in assets.assets_iter() { - Config::AssetTransactor::check_out(&dest, &asset, &self.context); - } - Ok(()) - })(); - if result.is_err() { - self.holding = old_holding; - } - result - }, - ReportHolding { response_info, assets } => { - // Note that we pass `None` as `maybe_failed_bin` since no assets were ever removed - // from Holding. - let assets = Self::reanchored(self.holding.min(&assets), &response_info.destination, None); - self.respond(self.cloned_origin(), Response::Assets(assets), response_info, FeeReason::Report)?; - Ok(()) - }, - BuyExecution { fees, weight_limit } => { - // There is no need to buy any weight if `weight_limit` is `Unlimited` since it - // would indicate that `AllowTopLevelPaidExecutionFrom` was unused for execution - // and thus there is some other reason why it has been determined that this XCM - // should be executed. - let Some(weight) = Option::::from(weight_limit) else { return Ok(()) }; - let old_holding = self.holding.clone(); - // pay for `weight` using up to `fees` of the holding register. - let max_fee = self.holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?; - log::trace!( - target: "xcm::execute", - "max_fee: {max_fee:?}", - ); - let result = || -> Result<(), XcmError> { - let unspent = self.trader.buy_weight(weight, max_fee, &self.context)?; - log::trace!( - target: "xcm::execute", - "unspent: {unspent:?}", - ); - self.holding.subsume_assets(unspent); - Ok(()) - }(); - if result.is_err() { - self.holding = old_holding; - } - result - }, - RefundSurplus => self.refund_surplus(), - SetErrorHandler(mut handler) => { - let handler_weight = - Config::Weigher::weight(&mut handler).map_err(|()| XcmError::WeightNotComputable)?; - self.total_surplus.saturating_accrue(self.error_handler_weight); - self.error_handler = handler; - self.error_handler_weight = handler_weight; - Ok(()) - }, - SetAppendix(mut appendix) => { - let appendix_weight = - Config::Weigher::weight(&mut appendix).map_err(|()| XcmError::WeightNotComputable)?; - self.total_surplus.saturating_accrue(self.appendix_weight); - self.appendix = appendix; - self.appendix_weight = appendix_weight; - Ok(()) - }, - ClearError => { - self.error = None; - Ok(()) - }, - ClaimAsset { assets, ticket } => { - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - self.ensure_can_subsume_assets(assets.len())?; - let ok = Config::AssetClaims::claim_assets(origin, &ticket, &assets, &self.context); - ensure!(ok, XcmError::UnknownClaim); - self.holding.subsume_assets(assets.into()); - Ok(()) - }, - Trap(code) => Err(XcmError::Trap(code)), - SubscribeVersion { query_id, max_response_weight } => { - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - // We don't allow derivative origins to subscribe since it would otherwise pose a - // DoS risk. - ensure!(&self.original_origin == origin, XcmError::BadOrigin); - Config::SubscriptionService::start(origin, query_id, max_response_weight, &self.context) - }, - UnsubscribeVersion => { - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - ensure!(&self.original_origin == origin, XcmError::BadOrigin); - Config::SubscriptionService::stop(origin, &self.context) - }, - BurnAsset(assets) => { - self.holding.saturating_take(assets.into()); - Ok(()) - }, - ExpectAsset(assets) => self.holding.ensure_contains(&assets).map_err(|_| XcmError::ExpectationFalse), - ExpectOrigin(origin) => { - ensure!(self.context.origin == origin, XcmError::ExpectationFalse); - Ok(()) - }, - ExpectError(error) => { - ensure!(self.error == error, XcmError::ExpectationFalse); - Ok(()) - }, - ExpectTransactStatus(transact_status) => { - ensure!(self.transact_status == transact_status, XcmError::ExpectationFalse); - Ok(()) - }, - QueryPallet { module_name, response_info } => { - let pallets = Config::PalletInstancesInfo::infos() - .into_iter() - .filter(|x| x.module_name.as_bytes() == &module_name[..]) - .map(|x| { - PalletInfo::new( - x.index as u32, - x.name.as_bytes().into(), - x.module_name.as_bytes().into(), - x.crate_version.major as u32, - x.crate_version.minor as u32, - x.crate_version.patch as u32, - ) - }) - .collect::, XcmError>>()?; - let QueryResponseInfo { destination, query_id, max_weight } = response_info; - let response = Response::PalletsInfo(pallets.try_into().map_err(|_| XcmError::Overflow)?); - let querier = Self::to_querier(self.cloned_origin(), &destination)?; - let instruction = QueryResponse { query_id, response, max_weight, querier }; - let message = Xcm(vec![instruction]); - self.send(destination, message, FeeReason::QueryPallet)?; - Ok(()) - }, - ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => { - let pallet = Config::PalletInstancesInfo::infos() - .into_iter() - .find(|x| x.index == index as usize) - .ok_or(XcmError::PalletNotFound)?; - ensure!(pallet.name.as_bytes() == &name[..], XcmError::NameMismatch); - ensure!(pallet.module_name.as_bytes() == &module_name[..], XcmError::NameMismatch); - let major = pallet.crate_version.major as u32; - ensure!(major == crate_major, XcmError::VersionIncompatible); - let minor = pallet.crate_version.minor as u32; - ensure!(minor >= min_crate_minor, XcmError::VersionIncompatible); - Ok(()) - }, - ReportTransactStatus(response_info) => { - self.respond( - self.cloned_origin(), - Response::DispatchResult(self.transact_status.clone()), - response_info, - FeeReason::Report, - )?; - Ok(()) - }, - ClearTransactStatus => { - self.transact_status = Default::default(); - Ok(()) - }, - UniversalOrigin(new_global) => { - let universal_location = Config::UniversalLocation::get(); - ensure!(universal_location.first() != Some(&new_global), XcmError::InvalidLocation); - let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?; - let origin_xform = (origin, new_global); - let ok = Config::UniversalAliases::contains(&origin_xform); - ensure!(ok, XcmError::InvalidLocation); - let (_, new_global) = origin_xform; - let new_origin = Junctions::from([new_global]).relative_to(&universal_location); - self.context.origin = Some(new_origin); - Ok(()) - }, - ExportMessage { network, destination, xcm } => { - // The actual message sent to the bridge for forwarding is prepended with - // `UniversalOrigin` and `DescendOrigin` in order to ensure that the message is - // executed with this Origin. - // - // Prepend the desired message with instructions which effectively rewrite the - // origin. - // - // This only works because the remote chain empowers the bridge - // to speak for the local network. - let origin = self.context.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone(); - let universal_source = - Config::UniversalLocation::get().within_global(origin).map_err(|()| XcmError::Unanchored)?; - let hash = (self.origin_ref(), &destination).using_encoded(blake2_128); - let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0); - // Hash identifies the lane on the exporter which we use. We use the pairwise - // combination of the origin and destination to ensure origin/destination pairs - // will generally have their own lanes. - let (ticket, fee) = validate_export::( - network, - channel, - universal_source, - destination.clone(), - xcm, - )?; - let old_holding = self.holding.clone(); - let result = Config::TransactionalProcessor::process(|| { - self.take_fee(fee, FeeReason::Export { network, destination })?; - let _ = Config::MessageExporter::deliver(ticket).defensive_proof( - "`deliver` called immediately after `validate_export`; \ - `take_fee` does not affect the validity of the ticket; qed", - ); - Ok(()) - }); - if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { - self.holding = old_holding; - } - result - }, - LockAsset { asset, unlocker } => { - let old_holding = self.holding.clone(); - let result = Config::TransactionalProcessor::process(|| { - let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?; - let (remote_asset, context) = Self::try_reanchor(asset.clone(), &unlocker)?; - let lock_ticket = Config::AssetLocker::prepare_lock(unlocker.clone(), asset, origin.clone())?; - let owner = origin.reanchored(&unlocker, &context).map_err(|_| XcmError::ReanchorFailed)?; - let msg = Xcm::<()>(vec![NoteUnlockable { asset: remote_asset, owner }]); - let (ticket, price) = validate_send::(unlocker, msg)?; - self.take_fee(price, FeeReason::LockAsset)?; - lock_ticket.enact()?; - Config::XcmSender::deliver(ticket)?; - Ok(()) - }); - if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { - self.holding = old_holding; - } - result - }, - UnlockAsset { asset, target } => { - let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?; - Config::AssetLocker::prepare_unlock(origin, asset, target)?.enact()?; - Ok(()) - }, - NoteUnlockable { asset, owner } => { - let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?; - Config::AssetLocker::note_unlockable(origin, asset, owner)?; - Ok(()) - }, - RequestUnlock { asset, locker } => { - let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?; - let remote_asset = Self::try_reanchor(asset.clone(), &locker)?.0; - let remote_target = Self::try_reanchor(origin.clone(), &locker)?.0; - let reduce_ticket = - Config::AssetLocker::prepare_reduce_unlockable(locker.clone(), asset, origin.clone())?; - let msg = Xcm::<()>(vec![UnlockAsset { asset: remote_asset, target: remote_target }]); - let (ticket, price) = validate_send::(locker, msg)?; - let old_holding = self.holding.clone(); - let result = Config::TransactionalProcessor::process(|| { - self.take_fee(price, FeeReason::RequestUnlock)?; - reduce_ticket.enact()?; - Config::XcmSender::deliver(ticket)?; - Ok(()) - }); - if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() { - self.holding = old_holding; - } - result - }, - ExchangeAsset { give, want, maximal } => { - let old_holding = self.holding.clone(); - let give = self.holding.saturating_take(give); - let result = (|| -> Result<(), XcmError> { - self.ensure_can_subsume_assets(want.len())?; - let exchange_result = - Config::AssetExchanger::exchange_asset(self.origin_ref(), give, &want, maximal); - if let Ok(received) = exchange_result { - self.holding.subsume_assets(received.into()); - Ok(()) - } else { - Err(XcmError::NoDeal) - } - })(); - if result.is_err() { - self.holding = old_holding; - } - result - }, - SetFeesMode { jit_withdraw } => { - self.fees_mode = FeesMode { jit_withdraw }; - Ok(()) - }, - SetTopic(topic) => { - self.context.topic = Some(topic); - Ok(()) - }, - ClearTopic => { - self.context.topic = None; - Ok(()) - }, - AliasOrigin(target) => { - let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - if Config::Aliasers::contains(origin, &target) { - self.context.origin = Some(target); - Ok(()) - } else { - Err(XcmError::NoPermission) - } - }, - UnpaidExecution { check_origin, .. } => { - ensure!(check_origin.is_none() || self.context.origin == check_origin, XcmError::BadOrigin); - Ok(()) - }, - HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => - Config::TransactionalProcessor::process(|| { - Config::HrmpNewChannelOpenRequestHandler::handle(sender, max_message_size, max_capacity) - }), - HrmpChannelAccepted { recipient } => - Config::TransactionalProcessor::process(|| Config::HrmpChannelAcceptedHandler::handle(recipient)), - HrmpChannelClosing { initiator, sender, recipient } => Config::TransactionalProcessor::process(|| { - Config::HrmpChannelClosingHandler::handle(initiator, sender, recipient) - }), - } - } -} diff --git a/debug-executor/src/traits/asset_exchange.rs b/debug-executor/src/traits/asset_exchange.rs deleted file mode 100644 index 432a7498e..000000000 --- a/debug-executor/src/traits/asset_exchange.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use crate::AssetsInHolding; -use xcm::prelude::*; - -/// A service for exchanging assets. -pub trait AssetExchange { - /// Handler for exchanging an asset. - /// - /// - `origin`: The location attempting the exchange; this should generally not matter. - /// - `give`: The assets which have been removed from the caller. - /// - `want`: The minimum amount of assets which should be given to the caller in case any - /// exchange happens. If more assets are provided, then they should generally be of the same - /// asset class if at all possible. - /// - `maximal`: If `true`, then as much as possible should be exchanged. - /// - /// `Ok` is returned along with the new set of assets which have been exchanged for `give`. At - /// least want must be in the set. Some assets originally in `give` may also be in this set. In - /// the case of returning an `Err`, then `give` is returned. - fn exchange_asset( - origin: Option<&Location>, - give: AssetsInHolding, - want: &Assets, - maximal: bool, - ) -> Result; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl AssetExchange for Tuple { - fn exchange_asset( - origin: Option<&Location>, - give: AssetsInHolding, - want: &Assets, - maximal: bool, - ) -> Result { - for_tuples!( #( - let give = match Tuple::exchange_asset(origin, give, want, maximal) { - Ok(r) => return Ok(r), - Err(a) => a, - }; - )* ); - Err(give) - } -} diff --git a/debug-executor/src/traits/asset_lock.rs b/debug-executor/src/traits/asset_lock.rs deleted file mode 100644 index f344c2b7d..000000000 --- a/debug-executor/src/traits/asset_lock.rs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use sp_std::convert::Infallible; -use xcm::prelude::*; - -#[derive(Debug)] -pub enum LockError { - NotApplicable, - WouldClobber, - BadOrigin, - NotLocked, - NotEnoughLocked, - Unimplemented, - NotTrusted, - BadOwner, - UnknownAsset, - AssetNotOwned, - NoResources, - UnexpectedState, - InUse, -} - -impl From for XcmError { - fn from(e: LockError) -> XcmError { - use LockError::*; - match e { - NotApplicable => XcmError::AssetNotFound, - BadOrigin => XcmError::BadOrigin, - WouldClobber | NotLocked | NotEnoughLocked | Unimplemented | NotTrusted | BadOwner | UnknownAsset | - AssetNotOwned | NoResources | UnexpectedState | InUse => XcmError::LockError, - } - } -} - -pub trait Enact { - /// Enact a lock. This should generally be infallible if called immediately after being - /// received. - fn enact(self) -> Result<(), LockError>; -} - -impl Enact for Infallible { - fn enact(self) -> Result<(), LockError> { - unreachable!() - } -} - -/// Define a handler for notification of an asset being locked and for the unlock instruction. -pub trait AssetLock { - /// `Enact` implementer for `prepare_lock`. This type may be dropped safely to avoid doing the - /// lock. - type LockTicket: Enact; - - /// `Enact` implementer for `prepare_unlock`. This type may be dropped safely to avoid doing the - /// unlock. - type UnlockTicket: Enact; - - /// `Enact` implementer for `prepare_reduce_unlockable`. This type may be dropped safely to - /// avoid doing the unlock. - type ReduceTicket: Enact; - - /// Prepare to lock an asset. On success, a `Self::LockTicket` it returned, which can be used - /// to actually enact the lock. - /// - /// WARNING: Don't call this with an undropped instance of `Self::LockTicket` or - /// `Self::UnlockTicket`. - fn prepare_lock(unlocker: Location, asset: Asset, owner: Location) -> Result; - - /// Prepare to unlock an asset. On success, a `Self::UnlockTicket` it returned, which can be - /// used to actually enact the lock. - /// - /// WARNING: Don't call this with an undropped instance of `Self::LockTicket` or - /// `Self::UnlockTicket`. - fn prepare_unlock(locker: Location, asset: Asset, owner: Location) -> Result; - - /// Handler for when a location reports to us that an asset has been locked for us to unlock - /// at a later stage. - /// - /// If there is no way to handle the lock report, then this should return an error so that the - /// sending chain can ensure the lock does not remain. - /// - /// We should only act upon this message if we believe that the `origin` is honest. - fn note_unlockable(locker: Location, asset: Asset, owner: Location) -> Result<(), LockError>; - - /// Handler for when an owner wishes to unlock an asset on a remote chain. - /// - /// Returns a ticket which can be used to actually note the reduction in unlockable assets that - /// `owner` commands on `locker`. - /// - /// WARNING: Don't call this with an undropped instance of `Self::ReduceTicket`. - fn prepare_reduce_unlockable( - locker: Location, - asset: Asset, - owner: Location, - ) -> Result; -} - -impl AssetLock for () { - type LockTicket = Infallible; - type ReduceTicket = Infallible; - type UnlockTicket = Infallible; - - fn prepare_lock(_: Location, _: Asset, _: Location) -> Result { - Err(LockError::NotApplicable) - } - - fn prepare_unlock(_: Location, _: Asset, _: Location) -> Result { - Err(LockError::NotApplicable) - } - - fn note_unlockable(_: Location, _: Asset, _: Location) -> Result<(), LockError> { - Err(LockError::NotApplicable) - } - - fn prepare_reduce_unlockable(_: Location, _: Asset, _: Location) -> Result { - Err(LockError::NotApplicable) - } -} diff --git a/debug-executor/src/traits/asset_transfer.rs b/debug-executor/src/traits/asset_transfer.rs deleted file mode 100644 index ccb0a1b3e..000000000 --- a/debug-executor/src/traits/asset_transfer.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use crate::traits::TransactAsset; -use frame_support::traits::ContainsPair; -use scale_info::TypeInfo; -use sp_runtime::codec::{Decode, Encode}; -use xcm::prelude::*; - -/// Errors related to determining asset transfer support. -#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)] -pub enum Error { - /// Reserve chain could not be determined for assets. - UnknownReserve, -} - -/// Specify which type of asset transfer is required for a particular `(asset, dest)` combination. -#[derive(Clone, Encode, Decode, PartialEq, Debug, TypeInfo)] -pub enum TransferType { - /// should teleport `asset` to `dest` - Teleport, - /// should reserve-transfer `asset` to `dest`, using local chain as reserve - LocalReserve, - /// should reserve-transfer `asset` to `dest`, using `dest` as reserve - DestinationReserve, - /// should reserve-transfer `asset` to `dest`, using remote chain `Location` as reserve - RemoteReserve(VersionedLocation), -} - -/// A trait for identifying asset transfer type based on `IsTeleporter` and `IsReserve` -/// configurations. -pub trait XcmAssetTransfers { - /// Combinations of (Asset, Location) pairs which we trust as reserves. Meaning - /// reserve-based-transfers are to be used for assets matching this filter. - type IsReserve: ContainsPair; - - /// Combinations of (Asset, Location) pairs which we trust as teleporters. Meaning teleports are - /// to be used for assets matching this filter. - type IsTeleporter: ContainsPair; - - /// How to withdraw and deposit an asset. - type AssetTransactor: TransactAsset; - - /// Determine transfer type to be used for transferring `asset` from local chain to `dest`. - fn determine_for(asset: &Asset, dest: &Location) -> Result { - if Self::IsTeleporter::contains(asset, dest) { - // we trust destination for teleporting asset - return Ok(TransferType::Teleport) - } else if Self::IsReserve::contains(asset, dest) { - // we trust destination as asset reserve location - return Ok(TransferType::DestinationReserve) - } - - // try to determine reserve location based on asset id/location - let asset_location = asset.id.0.chain_location(); - if asset_location == Location::here() || Self::IsTeleporter::contains(asset, &asset_location) { - // if the asset is local, then it's a local reserve - // it's also a local reserve if the asset's location is not `here` but it's a location - // where it can be teleported to `here` => local reserve - Ok(TransferType::LocalReserve) - } else if Self::IsReserve::contains(asset, &asset_location) { - // remote location that is recognized as reserve location for asset - Ok(TransferType::RemoteReserve(asset_location.into())) - } else { - // remote location that is not configured either as teleporter or reserve => cannot - // determine asset reserve - Err(Error::UnknownReserve) - } - } -} - -impl XcmAssetTransfers for () { - type AssetTransactor = (); - type IsReserve = (); - type IsTeleporter = (); - - fn determine_for(_: &Asset, _: &Location) -> Result { - return Err(Error::UnknownReserve); - } -} diff --git a/debug-executor/src/traits/conversion.rs b/debug-executor/src/traits/conversion.rs deleted file mode 100644 index dcfdbec32..000000000 --- a/debug-executor/src/traits/conversion.rs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use frame_support::traits::{Contains, OriginTrait}; -use sp_runtime::{traits::Dispatchable, DispatchErrorWithPostInfo}; -use sp_std::{marker::PhantomData, result::Result}; -use xcm::latest::prelude::*; - -/// Means of converting a location into an account identifier. -pub trait ConvertLocation { - /// Convert the `location` into `Some` account ID, or `None` if not possible. - fn convert_location(location: &Location) -> Option; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl ConvertLocation for Tuple { - fn convert_location(l: &Location) -> Option { - for_tuples!( #( - match Tuple::convert_location(l) { - Some(result) => return Some(result), - None => {}, - } - )* ); - None - } -} - -/// A converter `trait` for origin types. -/// -/// Can be amalgamated into tuples. If any of the tuple elements returns `Ok(_)`, it short circuits. -/// Else, the `Err(_)` of the last tuple item is returned. Each intermediate `Err(_)` might return a -/// different `origin` of type `Origin` which is passed to the next convert item. -/// -/// ```rust -/// # use xcm::latest::{Location, Junctions, Junction, OriginKind}; -/// # use staging_xcm_executor::traits::ConvertOrigin; -/// // A convertor that will bump the para id and pass it to the next one. -/// struct BumpParaId; -/// impl ConvertOrigin for BumpParaId { -/// fn convert_origin(origin: impl Into, _: OriginKind) -> Result { -/// match origin.into().unpack() { -/// (0, [Junction::Parachain(id)]) => { -/// Err([Junction::Parachain(id + 1)].into()) -/// } -/// _ => unreachable!() -/// } -/// } -/// } -/// -/// struct AcceptPara7; -/// impl ConvertOrigin for AcceptPara7 { -/// fn convert_origin(origin: impl Into, _: OriginKind) -> Result { -/// let origin = origin.into(); -/// match origin.unpack() { -/// (0, [Junction::Parachain(id)]) if *id == 7 => { -/// Ok(7) -/// } -/// _ => Err(origin) -/// } -/// } -/// } -/// # fn main() { -/// let origin: Location = [Junction::Parachain(6)].into(); -/// assert!( -/// <(BumpParaId, AcceptPara7) as ConvertOrigin>::convert_origin(origin, OriginKind::Native) -/// .is_ok() -/// ); -/// # } -/// ``` -pub trait ConvertOrigin { - /// Attempt to convert `origin` to the generic `Origin` whilst consuming it. - fn convert_origin(origin: impl Into, kind: OriginKind) -> Result; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl ConvertOrigin for Tuple { - fn convert_origin(origin: impl Into, kind: OriginKind) -> Result { - for_tuples!( #( - let origin = match Tuple::convert_origin(origin, kind) { - Err(o) => o, - r => return r - }; - )* ); - let origin = origin.into(); - log::trace!( - target: "xcm::convert_origin", - "could not convert: origin: {:?}, kind: {:?}", - origin, - kind, - ); - Err(origin) - } -} - -/// Defines how a call is dispatched with given origin. -/// Allows to customize call dispatch, such as adapting the origin based on the call -/// or modifying the call. -pub trait CallDispatcher { - fn dispatch( - call: Call, - origin: Call::RuntimeOrigin, - ) -> Result>; -} - -pub struct WithOriginFilter(PhantomData); -impl CallDispatcher for WithOriginFilter -where - Call: Dispatchable, - Call::RuntimeOrigin: OriginTrait, - <::RuntimeOrigin as OriginTrait>::Call: 'static, - Filter: Contains<<::RuntimeOrigin as OriginTrait>::Call> + 'static, -{ - fn dispatch( - call: Call, - mut origin: ::RuntimeOrigin, - ) -> Result<::PostInfo, DispatchErrorWithPostInfo<::PostInfo>> { - origin.add_filter(Filter::contains); - call.dispatch(origin) - } -} - -// We implement it for every calls so they can dispatch themselves -// (without any change). -impl CallDispatcher for Call { - fn dispatch( - call: Call, - origin: Call::RuntimeOrigin, - ) -> Result> { - call.dispatch(origin) - } -} diff --git a/debug-executor/src/traits/drop_assets.rs b/debug-executor/src/traits/drop_assets.rs deleted file mode 100644 index 9cb13e0dd..000000000 --- a/debug-executor/src/traits/drop_assets.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use crate::AssetsInHolding; -use core::marker::PhantomData; -use frame_support::traits::Contains; -use xcm::latest::{Assets, Location, Weight, XcmContext}; - -/// Define a handler for when some non-empty `AssetsInHolding` value should be dropped. -pub trait DropAssets { - /// Handler for receiving dropped assets. Returns the weight consumed by this operation. - fn drop_assets(origin: &Location, assets: AssetsInHolding, context: &XcmContext) -> Weight; -} -impl DropAssets for () { - fn drop_assets(_origin: &Location, _assets: AssetsInHolding, _context: &XcmContext) -> Weight { - Weight::zero() - } -} - -/// Morph a given `DropAssets` implementation into one which can filter based on assets. This can -/// be used to ensure that `AssetsInHolding` values which hold no value are ignored. -pub struct FilterAssets(PhantomData<(D, A)>); - -impl> DropAssets for FilterAssets { - fn drop_assets(origin: &Location, assets: AssetsInHolding, context: &XcmContext) -> Weight { - if A::contains(&assets) { - D::drop_assets(origin, assets, context) - } else { - Weight::zero() - } - } -} - -/// Morph a given `DropAssets` implementation into one which can filter based on origin. This can -/// be used to ban origins which don't have proper protections/policies against misuse of the -/// asset trap facility don't get to use it. -pub struct FilterOrigin(PhantomData<(D, O)>); - -impl> DropAssets for FilterOrigin { - fn drop_assets(origin: &Location, assets: AssetsInHolding, context: &XcmContext) -> Weight { - if O::contains(origin) { - D::drop_assets(origin, assets, context) - } else { - Weight::zero() - } - } -} - -/// Define any handlers for the `AssetClaim` instruction. -pub trait ClaimAssets { - /// Claim any assets available to `origin` and return them in a single `Assets` value, together - /// with the weight used by this operation. - fn claim_assets(origin: &Location, ticket: &Location, what: &Assets, context: &XcmContext) -> bool; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl ClaimAssets for Tuple { - fn claim_assets(origin: &Location, ticket: &Location, what: &Assets, context: &XcmContext) -> bool { - for_tuples!( #( - if Tuple::claim_assets(origin, ticket, what, context) { - return true; - } - )* ); - false - } -} diff --git a/debug-executor/src/traits/export.rs b/debug-executor/src/traits/export.rs deleted file mode 100644 index 2ddfcff73..000000000 --- a/debug-executor/src/traits/export.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use xcm::latest::prelude::*; - -/// Utility for delivering a message to a system under a different (non-local) consensus with a -/// spoofed origin. This essentially defines the behaviour of the `ExportMessage` XCM instruction. -/// -/// This is quite different to `SendXcm`; `SendXcm` assumes that the local side's location will be -/// preserved to be represented as the value of the Origin register in the messages execution. -/// -/// This trait on the other hand assumes that we do not necessarily want the Origin register to -/// contain the local (i.e. the caller chain's) location, since it will generally be exporting a -/// message on behalf of another consensus system. Therefore in addition to the message, the -/// destination must be given in two parts: the network and the interior location within it. -/// -/// We also require the caller to state exactly what location they purport to be representing. The -/// destination must accept the local location to represent that location or the operation will -/// fail. -pub trait ExportXcm { - /// Intermediate value which connects the two phases of the export operation. - type Ticket; - - /// Check whether the given `message` is deliverable to the given `destination` on `network`, - /// spoofing its source as `universal_source` and if so determine the cost which will be paid by - /// this chain to do so, returning a `Ticket` token which can be used to enact delivery. - /// - /// The `channel` to be used on the `network`'s export mechanism (bridge, probably) must also - /// be provided. - /// - /// The `destination` and `message` must be `Some` (or else an error will be returned) and they - /// may only be consumed if the `Err` is not `NotApplicable`. - /// - /// If it is not a destination which can be reached with this type but possibly could by others, - /// then this *MUST* return `NotApplicable`. Any other error will cause the tuple - /// implementation (used to compose routing systems from different delivery agents) to exit - /// early without trying alternative means of delivery. - fn validate( - network: NetworkId, - channel: u32, - universal_source: &mut Option, - destination: &mut Option, - message: &mut Option>, - ) -> SendResult; - - /// Actually carry out the delivery operation for a previously validated message sending. - /// - /// The implementation should do everything possible to ensure that this function is infallible - /// if called immediately after `validate`. Returning an error here would result in a price - /// paid without the service being delivered. - fn deliver(ticket: Self::Ticket) -> Result; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl ExportXcm for Tuple { - for_tuples! { type Ticket = (#( Option ),* ); } - - fn validate( - network: NetworkId, - channel: u32, - universal_source: &mut Option, - destination: &mut Option, - message: &mut Option>, - ) -> SendResult { - let mut maybe_cost: Option = None; - let one_ticket: Self::Ticket = (for_tuples! { #( - if maybe_cost.is_some() { - None - } else { - match Tuple::validate(network, channel, universal_source, destination, message) { - Err(SendError::NotApplicable) => None, - Err(e) => { return Err(e) }, - Ok((v, c)) => { - maybe_cost = Some(c); - Some(v) - }, - } - } - ),* }); - if let Some(cost) = maybe_cost { - Ok((one_ticket, cost)) - } else { - Err(SendError::NotApplicable) - } - } - - fn deliver(mut one_ticket: Self::Ticket) -> Result { - for_tuples!( #( - if let Some(validated) = one_ticket.Tuple.take() { - return Tuple::deliver(validated); - } - )* ); - Err(SendError::Unroutable) - } -} - -/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps -/// both in `Some` before passing them as as mutable references into `T::send_xcm`. -pub fn validate_export( - network: NetworkId, - channel: u32, - universal_source: InteriorLocation, - dest: InteriorLocation, - msg: Xcm<()>, -) -> SendResult { - T::validate(network, channel, &mut Some(universal_source), &mut Some(dest), &mut Some(msg)) -} - -/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps -/// both in `Some` before passing them as as mutable references into `T::send_xcm`. -/// -/// Returns either `Ok` with the price of the delivery, or `Err` with the reason why the message -/// could not be sent. -/// -/// Generally you'll want to validate and get the price first to ensure that the sender can pay it -/// before actually doing the delivery. -pub fn export_xcm( - network: NetworkId, - channel: u32, - universal_source: InteriorLocation, - dest: InteriorLocation, - msg: Xcm<()>, -) -> Result<(XcmHash, Assets), SendError> { - let (ticket, price) = T::validate(network, channel, &mut Some(universal_source), &mut Some(dest), &mut Some(msg))?; - let hash = T::deliver(ticket)?; - Ok((hash, price)) -} diff --git a/debug-executor/src/traits/fee_manager.rs b/debug-executor/src/traits/fee_manager.rs deleted file mode 100644 index b6e303daa..000000000 --- a/debug-executor/src/traits/fee_manager.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use xcm::prelude::*; - -/// Handle stuff to do with taking fees in certain XCM instructions. -pub trait FeeManager { - /// Determine if a fee should be waived. - fn is_waived(origin: Option<&Location>, r: FeeReason) -> bool; - - /// Do something with the fee which has been paid. Doing nothing here silently burns the - /// fees. - fn handle_fee(fee: Assets, context: Option<&XcmContext>, r: FeeReason); -} - -/// Context under which a fee is paid. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum FeeReason { - /// When a reporting instruction is called. - Report, - /// When the `TransferReserveAsset` instruction is called. - TransferReserveAsset, - /// When the `DepositReserveAsset` instruction is called. - DepositReserveAsset, - /// When the `InitiateReserveWithdraw` instruction is called. - InitiateReserveWithdraw, - /// When the `InitiateTeleport` instruction is called. - InitiateTeleport, - /// When the `QueryPallet` instruction is called. - QueryPallet, - /// When the `ExportMessage` instruction is called (and includes the network ID). - Export { network: NetworkId, destination: InteriorLocation }, - /// The `charge_fees` API. - ChargeFees, - /// When the `LockAsset` instruction is called. - LockAsset, - /// When the `RequestUnlock` instruction is called. - RequestUnlock, -} - -impl FeeManager for () { - fn is_waived(_: Option<&Location>, _: FeeReason) -> bool { - false - } - - fn handle_fee(_: Assets, _: Option<&XcmContext>, _: FeeReason) {} -} diff --git a/debug-executor/src/traits/filter_asset_location.rs b/debug-executor/src/traits/filter_asset_location.rs deleted file mode 100644 index 5d0c32890..000000000 --- a/debug-executor/src/traits/filter_asset_location.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use frame_support::traits::ContainsPair; -use xcm::latest::{Asset, Location}; - -/// Filters assets/location pairs. -/// -/// Can be amalgamated into tuples. If any item returns `true`, it short-circuits, else `false` is -/// returned. -#[deprecated = "Use `frame_support::traits::ContainsPair` instead"] -pub trait FilterAssetLocation { - /// A filter to distinguish between asset/location pairs. - fn contains(asset: &Asset, origin: &Location) -> bool; -} - -#[allow(deprecated)] -impl> FilterAssetLocation for T { - fn contains(asset: &Asset, origin: &Location) -> bool { - T::contains(asset, origin) - } -} diff --git a/debug-executor/src/traits/hrmp.rs b/debug-executor/src/traits/hrmp.rs deleted file mode 100644 index b6bbb9316..000000000 --- a/debug-executor/src/traits/hrmp.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use xcm::latest::Result as XcmResult; - -/// Executes logic when a `HrmpNewChannelOpenRequest` XCM notification is received. -pub trait HandleHrmpNewChannelOpenRequest { - fn handle(sender: u32, max_message_size: u32, max_capacity: u32) -> XcmResult; -} - -/// Executes optional logic when a `HrmpChannelAccepted` XCM notification is received. -pub trait HandleHrmpChannelAccepted { - fn handle(recipient: u32) -> XcmResult; -} - -/// Executes optional logic when a `HrmpChannelClosing` XCM notification is received. -pub trait HandleHrmpChannelClosing { - fn handle(initiator: u32, sender: u32, recipient: u32) -> XcmResult; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl HandleHrmpNewChannelOpenRequest for Tuple { - fn handle(sender: u32, max_message_size: u32, max_capacity: u32) -> XcmResult { - for_tuples!( #( Tuple::handle(sender, max_message_size, max_capacity)?; )* ); - Ok(()) - } -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl HandleHrmpChannelAccepted for Tuple { - fn handle(recipient: u32) -> XcmResult { - for_tuples!( #( Tuple::handle(recipient)?; )* ); - Ok(()) - } -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl HandleHrmpChannelClosing for Tuple { - fn handle(initiator: u32, sender: u32, recipient: u32) -> XcmResult { - for_tuples!( #( Tuple::handle(initiator, sender, recipient)?; )* ); - Ok(()) - } -} diff --git a/debug-executor/src/traits/mod.rs b/debug-executor/src/traits/mod.rs deleted file mode 100644 index 3095d2af8..000000000 --- a/debug-executor/src/traits/mod.rs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -//! Various traits used in configuring the executor. - -mod conversion; -pub use conversion::{CallDispatcher, ConvertLocation, ConvertOrigin, WithOriginFilter}; -mod drop_assets; -pub use drop_assets::{ClaimAssets, DropAssets}; -mod asset_exchange; -pub use asset_exchange::AssetExchange; -mod asset_lock; -pub use asset_lock::{AssetLock, Enact, LockError}; -mod asset_transfer; -pub use asset_transfer::{Error as AssetTransferError, TransferType, XcmAssetTransfers}; -mod export; -pub use export::{export_xcm, validate_export, ExportXcm}; -mod fee_manager; -pub use fee_manager::{FeeManager, FeeReason}; -mod filter_asset_location; -#[allow(deprecated)] -pub use filter_asset_location::FilterAssetLocation; -mod token_matching; -pub use token_matching::{Error, MatchesFungible, MatchesFungibles, MatchesNonFungible, MatchesNonFungibles}; -mod on_response; -pub use on_response::{OnResponse, QueryHandler, QueryResponseStatus, VersionChangeNotifier}; -mod process_transaction; -pub use process_transaction::ProcessTransaction; -mod should_execute; -pub use should_execute::{CheckSuspension, Properties, ShouldExecute}; -mod transact_asset; -pub use transact_asset::TransactAsset; -mod hrmp; -pub use hrmp::{HandleHrmpChannelAccepted, HandleHrmpChannelClosing, HandleHrmpNewChannelOpenRequest}; -mod record_xcm; -mod weight; -pub use record_xcm::RecordXcm; -#[deprecated = "Use `sp_runtime::traits::` instead"] -pub use sp_runtime::traits::{Identity, TryConvertInto as JustTry}; -pub use weight::{WeightBounds, WeightTrader}; - -pub mod prelude { - pub use super::{ - export_xcm, validate_export, AssetExchange, AssetLock, ClaimAssets, ConvertOrigin, DropAssets, Enact, Error, - ExportXcm, FeeManager, FeeReason, LockError, MatchesFungible, MatchesFungibles, MatchesNonFungible, - MatchesNonFungibles, OnResponse, ProcessTransaction, ShouldExecute, TransactAsset, VersionChangeNotifier, - WeightBounds, WeightTrader, WithOriginFilter, - }; - #[allow(deprecated)] - pub use super::{Identity, JustTry}; -} diff --git a/debug-executor/src/traits/on_response.rs b/debug-executor/src/traits/on_response.rs deleted file mode 100644 index fce5868ea..000000000 --- a/debug-executor/src/traits/on_response.rs +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use crate::{Junctions::Here, Xcm}; -use codec::{Decode, Encode}; -use core::result; -use frame_support::{pallet_prelude::Get, parameter_types}; -use sp_arithmetic::traits::Zero; -use sp_std::fmt::Debug; -use xcm::latest::{ - Error as XcmError, InteriorLocation, Location, QueryId, Response, Result as XcmResult, Weight, XcmContext, -}; - -/// Define what needs to be done upon receiving a query response. -pub trait OnResponse { - /// Returns `true` if we are expecting a response from `origin` for query `query_id` that was - /// queried by `querier`. - fn expecting_response(origin: &Location, query_id: u64, querier: Option<&Location>) -> bool; - /// Handler for receiving a `response` from `origin` relating to `query_id` initiated by - /// `querier`. - fn on_response( - origin: &Location, - query_id: u64, - querier: Option<&Location>, - response: Response, - max_weight: Weight, - context: &XcmContext, - ) -> Weight; -} -impl OnResponse for () { - fn expecting_response(_origin: &Location, _query_id: u64, _querier: Option<&Location>) -> bool { - false - } - - fn on_response( - _origin: &Location, - _query_id: u64, - _querier: Option<&Location>, - _response: Response, - _max_weight: Weight, - _context: &XcmContext, - ) -> Weight { - Weight::zero() - } -} - -/// Trait for a type which handles notifying a destination of XCM version changes. -pub trait VersionChangeNotifier { - /// Start notifying `location` should the XCM version of this chain change. - /// - /// When it does, this type should ensure a `QueryResponse` message is sent with the given - /// `query_id` & `max_weight` and with a `response` of `Response::Version`. This should happen - /// until/unless `stop` is called with the correct `query_id`. - /// - /// If the `location` has an ongoing notification and when this function is called, then an - /// error should be returned. - fn start(location: &Location, query_id: QueryId, max_weight: Weight, context: &XcmContext) -> XcmResult; - - /// Stop notifying `location` should the XCM change. Returns an error if there is no existing - /// notification set up. - fn stop(location: &Location, context: &XcmContext) -> XcmResult; - - /// Return true if a location is subscribed to XCM version changes. - fn is_subscribed(location: &Location) -> bool; -} - -impl VersionChangeNotifier for () { - fn start(_: &Location, _: QueryId, _: Weight, _: &XcmContext) -> XcmResult { - Err(XcmError::Unimplemented) - } - - fn stop(_: &Location, _: &XcmContext) -> XcmResult { - Err(XcmError::Unimplemented) - } - - fn is_subscribed(_: &Location) -> bool { - false - } -} - -/// The possible state of an XCM query response. -#[derive(Debug, PartialEq, Eq, Encode, Decode)] -pub enum QueryResponseStatus { - /// The response has arrived, and includes the inner Response and the block number it arrived - /// at. - Ready { response: Response, at: BlockNumber }, - /// The response has not yet arrived, the XCM might still be executing or the response might be - /// in transit. - Pending { timeout: BlockNumber }, - /// No response with the given `QueryId` was found, or the response was already queried and - /// removed from local storage. - NotFound, - /// Got an unexpected XCM version. - UnexpectedVersion, -} - -/// Provides methods to expect responses from XCMs and query their status. -pub trait QueryHandler { - type BlockNumber: Zero + Encode; - type Error; - type UniversalLocation: Get; - - /// Attempt to create a new query ID and register it as a query that is yet to respond. - fn new_query( - responder: impl Into, - timeout: Self::BlockNumber, - match_querier: impl Into, - ) -> QueryId; - - /// Consume `message` and return another which is equivalent to it except that it reports - /// back the outcome. - /// - /// - `message`: The message whose outcome should be reported. - /// - `responder`: The origin from which a response should be expected. - /// - `timeout`: The block number after which it is permissible to return `NotFound` from - /// `take_response`. - /// - /// `report_outcome` may return an error if the `responder` is not invertible. - /// - /// It is assumed that the querier of the response will be `Here`. - /// The response can be queried with `take_response`. - fn report_outcome( - message: &mut Xcm<()>, - responder: impl Into, - timeout: Self::BlockNumber, - ) -> result::Result; - - /// Attempt to remove and return the response of query with ID `query_id`. - fn take_response(id: QueryId) -> QueryResponseStatus; - - /// Makes sure to expect a response with the given id. - #[cfg(feature = "runtime-benchmarks")] - fn expect_response(id: QueryId, response: Response); -} - -parameter_types! { - pub UniversalLocation: InteriorLocation = Here; -} - -impl QueryHandler for () { - type BlockNumber = u64; - type Error = (); - type UniversalLocation = UniversalLocation; - - fn take_response(_query_id: QueryId) -> QueryResponseStatus { - QueryResponseStatus::NotFound - } - - fn new_query( - _responder: impl Into, - _timeout: Self::BlockNumber, - _match_querier: impl Into, - ) -> QueryId { - 0u64 - } - - fn report_outcome( - _message: &mut Xcm<()>, - _responder: impl Into, - _timeout: Self::BlockNumber, - ) -> Result { - Err(()) - } - - #[cfg(feature = "runtime-benchmarks")] - fn expect_response(_id: QueryId, _response: crate::Response) {} -} diff --git a/debug-executor/src/traits/process_transaction.rs b/debug-executor/src/traits/process_transaction.rs deleted file mode 100644 index 246ae5def..000000000 --- a/debug-executor/src/traits/process_transaction.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use xcm::latest::prelude::*; - -/// Provides mechanisms for transactional processing of XCM instructions. -/// -/// This trait defines the behavior required to process XCM instructions in a transactional -/// manner. Implementers of this trait can ensure that XCM instructions are executed -/// atomically, meaning they either fully succeed or fully fail without any partial effects. -/// -/// Implementers of this trait can also choose to not process XCM instructions transactionally. -/// This is useful for cases where the implementer is not able to provide transactional guarantees. -/// In this case the `IS_TRANSACTIONAL` constant should be set to `false`. -/// The `()` type implements this trait in a non-transactional manner. -pub trait ProcessTransaction { - /// Whether or not the implementor of the this trait is actually transactional. - const IS_TRANSACTIONAL: bool; - - /// Processes an XCM instruction encapsulated within the provided closure. Responsible for - /// processing an XCM instruction transactionally. If the closure returns an error, any - /// changes made during its execution should be rolled back. In the case where the - /// implementer is not able to provide transactional guarantees, the closure should be - /// executed as is. - /// # Parameters - /// - `f`: A closure that encapsulates the XCM instruction being processed. It will return a - /// `Result` indicating the success or failure of the instruction. - /// - /// # Returns - /// - A `Result` indicating the overall success or failure of the transactional process. - fn process(f: F) -> Result<(), XcmError> - where - F: FnOnce() -> Result<(), XcmError>; -} - -impl ProcessTransaction for () { - const IS_TRANSACTIONAL: bool = false; - - fn process(f: F) -> Result<(), XcmError> - where - F: FnOnce() -> Result<(), XcmError>, - { - f() - } -} diff --git a/debug-executor/src/traits/record_xcm.rs b/debug-executor/src/traits/record_xcm.rs deleted file mode 100644 index d0b5bf92d..000000000 --- a/debug-executor/src/traits/record_xcm.rs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -//! Trait for recording XCMs and a dummy implementation. - -use xcm::latest::Xcm; - -/// Trait for recording XCMs. -pub trait RecordXcm { - /// Whether or not we should record incoming XCMs. - fn should_record() -> bool; - /// Enable or disable recording. - fn set_record_xcm(enabled: bool); - /// Get recorded XCM. - /// Returns `None` if no message was sent, or if recording was off. - fn recorded_xcm() -> Option>; - /// Record `xcm`. - fn record(xcm: Xcm<()>); -} - -impl RecordXcm for () { - fn should_record() -> bool { - false - } - - fn set_record_xcm(_: bool) {} - - fn recorded_xcm() -> Option> { - None - } - - fn record(_: Xcm<()>) {} -} diff --git a/debug-executor/src/traits/should_execute.rs b/debug-executor/src/traits/should_execute.rs deleted file mode 100644 index e76d56bfe..000000000 --- a/debug-executor/src/traits/should_execute.rs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use frame_support::traits::ProcessMessageError; -use sp_std::result::Result; -use xcm::latest::{Instruction, Location, Weight, XcmHash}; - -/// Properties of an XCM message and its imminent execution. -#[derive(Clone, Eq, PartialEq, Debug)] -pub struct Properties { - /// The amount of weight that the system has determined this - /// message may utilize in its execution. Typically non-zero only because of prior fee - /// payment, but could in principle be due to other factors. - pub weight_credit: Weight, - /// The identity of the message, if one is known. If left as `None`, then it will generally - /// default to the hash of the message which may be non-unique. - pub message_id: Option, -} - -/// Trait to determine whether the execution engine should actually execute a given XCM. -/// -/// Can be amalgamated into a tuple to have multiple trials. If any of the tuple elements returns -/// `Ok(())`, the execution stops. Else, `Err(_)` is returned if all elements reject the message. -pub trait ShouldExecute { - /// Returns `Ok(())` if the given `message` may be executed. - /// - /// - `origin`: The origin (sender) of the message. - /// - `instructions`: The message itself. - /// - `max_weight`: The (possibly over-) estimation of the weight of execution of the message. - /// - `properties`: Various pre-established properties of the message which may be mutated by - /// this API. - fn should_execute( - origin: &Location, - instructions: &mut [Instruction], - max_weight: Weight, - properties: &mut Properties, - ) -> Result<(), ProcessMessageError>; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl ShouldExecute for Tuple { - fn should_execute( - origin: &Location, - instructions: &mut [Instruction], - max_weight: Weight, - properties: &mut Properties, - ) -> Result<(), ProcessMessageError> { - for_tuples!( #( - match Tuple::should_execute(origin, instructions, max_weight, properties) { - Ok(()) => return Ok(()), - _ => (), - } - )* ); - log::trace!( - target: "xcm::should_execute", - "did not pass barrier: origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}", - origin, - instructions, - max_weight, - properties, - ); - Err(ProcessMessageError::Unsupported) - } -} - -/// Trait to determine whether the execution engine is suspended from executing a given XCM. -/// -/// The trait method is given the same parameters as `ShouldExecute::should_execute`, so that the -/// implementer will have all the context necessary to determine whether or not to suspend the -/// XCM executor. -/// -/// Can be chained together in tuples to have multiple rounds of checks. If all of the tuple -/// elements returns false, then execution is not suspended. Otherwise, execution is suspended -/// if any of the tuple elements returns true. -pub trait CheckSuspension { - fn is_suspended( - origin: &Location, - instructions: &mut [Instruction], - max_weight: Weight, - properties: &mut Properties, - ) -> bool; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl CheckSuspension for Tuple { - fn is_suspended( - origin: &Location, - instruction: &mut [Instruction], - max_weight: Weight, - properties: &mut Properties, - ) -> bool { - for_tuples!( #( - if Tuple::is_suspended(origin, instruction, max_weight, properties) { - return true - } - )* ); - - false - } -} diff --git a/debug-executor/src/traits/token_matching.rs b/debug-executor/src/traits/token_matching.rs deleted file mode 100644 index 6300b00aa..000000000 --- a/debug-executor/src/traits/token_matching.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use sp_std::result; -use xcm::latest::prelude::*; - -pub trait MatchesFungible { - fn matches_fungible(a: &Asset) -> Option; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl MatchesFungible for Tuple { - fn matches_fungible(a: &Asset) -> Option { - for_tuples!( #( - match Tuple::matches_fungible(a) { o @ Some(_) => return o, _ => () } - )* ); - log::trace!(target: "xcm::matches_fungible", "did not match fungible asset: {:?}", &a); - None - } -} - -pub trait MatchesNonFungible { - fn matches_nonfungible(a: &Asset) -> Option; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl MatchesNonFungible for Tuple { - fn matches_nonfungible(a: &Asset) -> Option { - for_tuples!( #( - match Tuple::matches_nonfungible(a) { o @ Some(_) => return o, _ => () } - )* ); - log::trace!(target: "xcm::matches_non_fungible", "did not match non-fungible asset: {:?}", &a); - None - } -} - -/// Errors associated with [`MatchesFungibles`] operation. -#[derive(Debug, PartialEq, Eq)] -pub enum Error { - /// The given asset is not handled. (According to [`XcmError::AssetNotFound`]) - AssetNotHandled, - /// `Location` to `AccountId` conversion failed. - AccountIdConversionFailed, - /// `u128` amount to currency `Balance` conversion failed. - AmountToBalanceConversionFailed, - /// `Location` to `AssetId`/`ClassId` conversion failed. - AssetIdConversionFailed, - /// `AssetInstance` to non-fungibles instance ID conversion failed. - InstanceConversionFailed, -} - -impl From for XcmError { - fn from(e: Error) -> Self { - use XcmError::FailedToTransactAsset; - match e { - Error::AssetNotHandled => XcmError::AssetNotFound, - Error::AccountIdConversionFailed => FailedToTransactAsset("AccountIdConversionFailed"), - Error::AmountToBalanceConversionFailed => FailedToTransactAsset("AmountToBalanceConversionFailed"), - Error::AssetIdConversionFailed => FailedToTransactAsset("AssetIdConversionFailed"), - Error::InstanceConversionFailed => FailedToTransactAsset("InstanceConversionFailed"), - } - } -} - -pub trait MatchesFungibles { - fn matches_fungibles(a: &Asset) -> result::Result<(AssetId, Balance), Error>; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl MatchesFungibles for Tuple { - fn matches_fungibles(a: &Asset) -> result::Result<(AssetId, Balance), Error> { - for_tuples!( #( - match Tuple::matches_fungibles(a) { o @ Ok(_) => return o, _ => () } - )* ); - log::trace!(target: "xcm::matches_fungibles", "did not match fungibles asset: {:?}", &a); - Err(Error::AssetNotHandled) - } -} - -pub trait MatchesNonFungibles { - fn matches_nonfungibles(a: &Asset) -> result::Result<(AssetId, Instance), Error>; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl MatchesNonFungibles for Tuple { - fn matches_nonfungibles(a: &Asset) -> result::Result<(AssetId, Instance), Error> { - for_tuples!( #( - match Tuple::matches_nonfungibles(a) { o @ Ok(_) => return o, _ => () } - )* ); - log::trace!(target: "xcm::matches_non_fungibles", "did not match fungibles asset: {:?}", &a); - Err(Error::AssetNotHandled) - } -} diff --git a/debug-executor/src/traits/transact_asset.rs b/debug-executor/src/traits/transact_asset.rs deleted file mode 100644 index b2c14c694..000000000 --- a/debug-executor/src/traits/transact_asset.rs +++ /dev/null @@ -1,411 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use crate::AssetsInHolding; -use sp_std::result::Result; -use xcm::latest::{Asset, Error as XcmError, Location, Result as XcmResult, XcmContext}; - -/// Facility for asset transacting. -/// -/// This should work with as many asset/location combinations as possible. Locations to support may -/// include non-account locations such as a `[Junction::Parachain]`. Different -/// chains may handle them in different ways. -/// -/// Can be amalgamated as a tuple of items that implement this trait. In such executions, if any of -/// the transactors returns `Ok(())`, then it will short circuit. Else, execution is passed to the -/// next transactor. -pub trait TransactAsset { - /// Ensure that `check_in` will do as expected. - /// - /// When composed as a tuple, all type-items are called and at least one must result in `Ok`. - fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Err(XcmError::Unimplemented) - } - - /// An asset has been teleported in from the given origin. This should do whatever housekeeping - /// is needed. - /// - /// NOTE: This will make only a best-effort at bookkeeping. The caller should ensure that - /// `can_check_in` has returned with `Ok` in order to guarantee that this operation proceeds - /// properly. - /// - /// Implementation note: In general this will do one of two things: On chains where the asset is - /// native, it will reduce the assets from a special "teleported" account so that a) - /// total-issuance is preserved; and b) to ensure that no more assets can be teleported in than - /// were teleported out overall (this should not be needed if the teleporting chains are to be - /// trusted, but better to be safe than sorry). On chains where the asset is not native then it - /// will generally just be a no-op. - /// - /// When composed as a tuple, all type-items are called. It is up to the implementer that there - /// exists no value for `_what` which can cause side-effects for more than one of the - /// type-items. - fn check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) {} - - /// Ensure that `check_out` will do as expected. - /// - /// When composed as a tuple, all type-items are called and at least one must result in `Ok`. - fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Err(XcmError::Unimplemented) - } - - /// An asset has been teleported out to the given destination. This should do whatever - /// housekeeping is needed. - /// - /// Implementation note: In general this will do one of two things: On chains where the asset is - /// native, it will increase the assets in a special "teleported" account so that a) - /// total-issuance is preserved; and b) to ensure that no more assets can be teleported in than - /// were teleported out overall (this should not be needed if the teleporting chains are to be - /// trusted, but better to be safe than sorry). On chains where the asset is not native then it - /// will generally just be a no-op. - /// - /// When composed as a tuple, all type-items are called. It is up to the implementer that there - /// exists no value for `_what` which can cause side-effects for more than one of the - /// type-items. - fn check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) {} - - /// Deposit the `what` asset into the account of `who`. - /// - /// Implementations should return `XcmError::FailedToTransactAsset` if deposit failed. - fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult { - Err(XcmError::Unimplemented) - } - - /// Withdraw the given asset from the consensus system. Return the actual asset(s) withdrawn, - /// which should always be equal to `_what`. - /// - /// The XCM `_maybe_context` parameter may be `None` when the caller of `withdraw_asset` is - /// outside of the context of a currently-executing XCM. An example will be the `charge_fees` - /// method in the XCM executor. - /// - /// Implementations should return `XcmError::FailedToTransactAsset` if withdraw failed. - fn withdraw_asset( - _what: &Asset, - _who: &Location, - _maybe_context: Option<&XcmContext>, - ) -> Result { - Err(XcmError::Unimplemented) - } - - /// Move an `asset` `from` one location in `to` another location. - /// - /// Returns `XcmError::FailedToTransactAsset` if transfer failed. - /// - /// ## Notes - /// This function is meant to only be implemented by the type implementing `TransactAsset`, and - /// not be called directly. Most common API usages will instead call `transfer_asset`, which in - /// turn has a default implementation that calls `internal_transfer_asset`. As such, **please - /// do not call this method directly unless you know what you're doing**. - fn internal_transfer_asset( - _asset: &Asset, - _from: &Location, - _to: &Location, - _context: &XcmContext, - ) -> Result { - Err(XcmError::Unimplemented) - } - - /// Move an `asset` `from` one location in `to` another location. - /// - /// Attempts to use `internal_transfer_asset` and if not available then falls back to using a - /// two-part withdraw/deposit. - fn transfer_asset( - asset: &Asset, - from: &Location, - to: &Location, - context: &XcmContext, - ) -> Result { - match Self::internal_transfer_asset(asset, from, to, context) { - Err(XcmError::AssetNotFound | XcmError::Unimplemented) => { - let assets = Self::withdraw_asset(asset, from, Some(context))?; - // Not a very forgiving attitude; once we implement roll-backs then it'll be nicer. - Self::deposit_asset(asset, to, Some(context))?; - Ok(assets) - }, - result => result, - } - } -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl TransactAsset for Tuple { - fn can_check_in(origin: &Location, what: &Asset, context: &XcmContext) -> XcmResult { - for_tuples!( #( - match Tuple::can_check_in(origin, what, context) { - Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (), - r => return r, - } - )* ); - log::trace!( - target: "xcm::TransactAsset::can_check_in", - "asset not found: what: {:?}, origin: {:?}, context: {:?}", - what, - origin, - context, - ); - Err(XcmError::AssetNotFound) - } - - fn check_in(origin: &Location, what: &Asset, context: &XcmContext) { - for_tuples!( #( - Tuple::check_in(origin, what, context); - )* ); - } - - fn can_check_out(dest: &Location, what: &Asset, context: &XcmContext) -> XcmResult { - for_tuples!( #( - match Tuple::can_check_out(dest, what, context) { - Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (), - r => return r, - } - )* ); - log::trace!( - target: "xcm::TransactAsset::can_check_out", - "asset not found: what: {:?}, dest: {:?}, context: {:?}", - what, - dest, - context, - ); - Err(XcmError::AssetNotFound) - } - - fn check_out(dest: &Location, what: &Asset, context: &XcmContext) { - for_tuples!( #( - Tuple::check_out(dest, what, context); - )* ); - } - - fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { - for_tuples!( #( - match Tuple::deposit_asset(what, who, context) { - Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (), - r => return r, - } - )* ); - log::trace!( - target: "xcm::TransactAsset::deposit_asset", - "did not deposit asset: what: {:?}, who: {:?}, context: {:?}", - what, - who, - context, - ); - Err(XcmError::AssetNotFound) - } - - fn withdraw_asset( - what: &Asset, - who: &Location, - maybe_context: Option<&XcmContext>, - ) -> Result { - for_tuples!( #( - match Tuple::withdraw_asset(what, who, maybe_context) { - Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (), - r => return r, - } - )* ); - log::trace!( - target: "xcm::TransactAsset::withdraw_asset", - "did not withdraw asset: what: {:?}, who: {:?}, maybe_context: {:?}", - what, - who, - maybe_context, - ); - Err(XcmError::AssetNotFound) - } - - fn internal_transfer_asset( - what: &Asset, - from: &Location, - to: &Location, - context: &XcmContext, - ) -> Result { - for_tuples!( #( - match Tuple::internal_transfer_asset(what, from, to, context) { - Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (), - r => return r, - } - )* ); - log::trace!( - target: "xcm::TransactAsset::internal_transfer_asset", - "did not transfer asset: what: {:?}, from: {:?}, to: {:?}, context: {:?}", - what, - from, - to, - context, - ); - Err(XcmError::AssetNotFound) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use xcm::latest::Junctions::Here; - - pub struct UnimplementedTransactor; - impl TransactAsset for UnimplementedTransactor {} - - pub struct NotFoundTransactor; - impl TransactAsset for NotFoundTransactor { - fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Err(XcmError::AssetNotFound) - } - - fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Err(XcmError::AssetNotFound) - } - - fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult { - Err(XcmError::AssetNotFound) - } - - fn withdraw_asset( - _what: &Asset, - _who: &Location, - _context: Option<&XcmContext>, - ) -> Result { - Err(XcmError::AssetNotFound) - } - - fn internal_transfer_asset( - _what: &Asset, - _from: &Location, - _to: &Location, - _context: &XcmContext, - ) -> Result { - Err(XcmError::AssetNotFound) - } - } - - pub struct OverflowTransactor; - impl TransactAsset for OverflowTransactor { - fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Err(XcmError::Overflow) - } - - fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Err(XcmError::Overflow) - } - - fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult { - Err(XcmError::Overflow) - } - - fn withdraw_asset( - _what: &Asset, - _who: &Location, - _context: Option<&XcmContext>, - ) -> Result { - Err(XcmError::Overflow) - } - - fn internal_transfer_asset( - _what: &Asset, - _from: &Location, - _to: &Location, - _context: &XcmContext, - ) -> Result { - Err(XcmError::Overflow) - } - } - - pub struct SuccessfulTransactor; - impl TransactAsset for SuccessfulTransactor { - fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Ok(()) - } - - fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Ok(()) - } - - fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult { - Ok(()) - } - - fn withdraw_asset( - _what: &Asset, - _who: &Location, - _context: Option<&XcmContext>, - ) -> Result { - Ok(AssetsInHolding::default()) - } - - fn internal_transfer_asset( - _what: &Asset, - _from: &Location, - _to: &Location, - _context: &XcmContext, - ) -> Result { - Ok(AssetsInHolding::default()) - } - } - - #[test] - fn defaults_to_asset_not_found() { - type MultiTransactor = (UnimplementedTransactor, NotFoundTransactor, UnimplementedTransactor); - - assert_eq!( - MultiTransactor::deposit_asset( - &(Here, 1u128).into(), - &Here.into(), - Some(&XcmContext::with_message_id([0; 32])), - ), - Err(XcmError::AssetNotFound) - ); - } - - #[test] - fn unimplemented_and_not_found_continue_iteration() { - type MultiTransactor = (UnimplementedTransactor, NotFoundTransactor, SuccessfulTransactor); - - assert_eq!( - MultiTransactor::deposit_asset( - &(Here, 1u128).into(), - &Here.into(), - Some(&XcmContext::with_message_id([0; 32])), - ), - Ok(()) - ); - } - - #[test] - fn unexpected_error_stops_iteration() { - type MultiTransactor = (OverflowTransactor, SuccessfulTransactor); - - assert_eq!( - MultiTransactor::deposit_asset( - &(Here, 1u128).into(), - &Here.into(), - Some(&XcmContext::with_message_id([0; 32])), - ), - Err(XcmError::Overflow) - ); - } - - #[test] - fn success_stops_iteration() { - type MultiTransactor = (SuccessfulTransactor, OverflowTransactor); - - assert_eq!( - MultiTransactor::deposit_asset( - &(Here, 1u128).into(), - &Here.into(), - Some(&XcmContext::with_message_id([0; 32])), - ), - Ok(()), - ); - } -} diff --git a/debug-executor/src/traits/weight.rs b/debug-executor/src/traits/weight.rs deleted file mode 100644 index df7be6668..000000000 --- a/debug-executor/src/traits/weight.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -use crate::AssetsInHolding; -use sp_std::result::Result; -use xcm::latest::{prelude::*, Weight}; - -/// Determine the weight of an XCM message. -pub trait WeightBounds { - /// Return the maximum amount of weight that an attempted execution of this message could - /// consume. - fn weight(message: &mut Xcm) -> Result; - - /// Return the maximum amount of weight that an attempted execution of this instruction could - /// consume. - fn instr_weight(instruction: &Instruction) -> Result; -} - -#[allow(unused)] -/// A means of getting approximate weight consumption for a given destination message executor and a -/// message. -pub trait UniversalWeigher { - /// Get the upper limit of weight required for `dest` to execute `message`. - fn weigh(dest: impl Into, message: Xcm<()>) -> Result; -} - -/// Charge for weight in order to execute XCM. -/// -/// A `WeightTrader` may also be put into a tuple, in which case the default behavior of -/// `buy_weight` and `refund_weight` would be to attempt to call each tuple element's own -/// implementation of these two functions, in the order of which they appear in the tuple, -/// returning early when a successful result is returned. -pub trait WeightTrader: Sized { - /// Create a new trader instance. - fn new() -> Self; - - /// Purchase execution weight credit in return for up to a given `payment`. If less of the - /// payment is required then the surplus is returned. If the `payment` cannot be used to pay - /// for the `weight`, then an error is returned. - fn buy_weight( - &mut self, - weight: Weight, - payment: AssetsInHolding, - context: &XcmContext, - ) -> Result; - - /// Attempt a refund of `weight` into some asset. The caller does not guarantee that the weight - /// was purchased using `buy_weight`. - /// - /// Default implementation refunds nothing. - fn refund_weight(&mut self, _weight: Weight, _context: &XcmContext) -> Option { - None - } -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl WeightTrader for Tuple { - fn new() -> Self { - for_tuples!( ( #( Tuple::new() ),* ) ) - } - - fn buy_weight( - &mut self, - weight: Weight, - payment: AssetsInHolding, - context: &XcmContext, - ) -> Result { - let mut too_expensive_error_found = false; - let mut last_error = None; - for_tuples!( #( - match Tuple.buy_weight(weight, payment.clone(), context) { - Ok(assets) => return Ok(assets), - Err(e) => { - if let XcmError::TooExpensive = e { - too_expensive_error_found = true; - } - last_error = Some(e) - } - } - )* ); - - log::trace!(target: "xcm::buy_weight", "last_error: {:?}, too_expensive_error_found: {}", last_error, too_expensive_error_found); - - // if we have multiple traders, and first one returns `TooExpensive` and others fail e.g. - // `AssetNotFound` then it is more accurate to return `TooExpensive` then `AssetNotFound` - Err(if too_expensive_error_found { - XcmError::TooExpensive - } else { - last_error.unwrap_or(XcmError::TooExpensive) - }) - } - - fn refund_weight(&mut self, weight: Weight, context: &XcmContext) -> Option { - for_tuples!( #( - if let Some(asset) = Tuple.refund_weight(weight, context) { - return Some(asset); - } - )* ); - None - } -} diff --git a/integration-tests/chopsticks/overrides/polimec.ts b/integration-tests/chopsticks/overrides/polimec.ts index 79eded3a0..70d9007ed 100644 --- a/integration-tests/chopsticks/overrides/polimec.ts +++ b/integration-tests/chopsticks/overrides/polimec.ts @@ -23,25 +23,25 @@ const dot_location = { }, }; -const weth_location = { +export const weth_location = { parents: 2, interior: { x2: [ { globalConsensus: { ethereum: { - chainId: 1n - } - } + chainId: 1n, + }, + }, }, { accountKey20: { - key: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" - } - } - ] - } -} + key: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + }, + }, + ], + }, +}; export const polimec_storage = { System: { @@ -85,57 +85,6 @@ export const polimec_storage = { ], ], Asset: [ - // [ - // [usdc_location], - // { - // owner: Accounts.ALICE, - // issuer: Accounts.ALICE, - // admin: Accounts.ALICE, - // freezer: Accounts.ALICE, - // supply: INITIAL_BALANCES.USDC, - // deposit: 0n, - // min_balance: 70000n, - // is_sufficient: true, - // accounts: 1, - // sufficients: 1, - // approvals: 0, - // status: 'Live', - // }, - // ], - // [ - // [usdt_location], - // { - // owner: Accounts.ALICE, - // issuer: Accounts.ALICE, - // admin: Accounts.ALICE, - // freezer: Accounts.ALICE, - // supply: INITIAL_BALANCES.USDT, - // deposit: 0n, - // min_balance: 70000n, - // is_sufficient: true, - // accounts: 1, - // sufficients: 1, - // approvals: 0, - // status: 'Live', - // }, - // ], - // [ - // [dot_location], - // { - // owner: Accounts.ALICE, - // issuer: Accounts.ALICE, - // admin: Accounts.ALICE, - // freezer: Accounts.ALICE, - // supply: INITIAL_BALANCES.DOT, - // deposit: 0n, - // min_balance: 100000000n, - // is_sufficient: true, - // accounts: 1, - // sufficients: 1, - // approvals: 0, - // status: 'Live', - // }, - // ], [ [weth_location], { @@ -143,9 +92,9 @@ export const polimec_storage = { issuer: Accounts.ALICE, admin: Accounts.ALICE, freezer: Accounts.ALICE, - supply: INITIAL_BALANCES.WETH, + supply: 100n * INITIAL_BALANCES.WETH, deposit: 0n, - min_balance: 1000000n, + min_balance: 15000000000000n, is_sufficient: true, accounts: 1, sufficients: 1, @@ -155,10 +104,7 @@ export const polimec_storage = { ], ], Metadata: [ - // [[usdc_location], { symbol: 'USDC', name: 'USDC', decimals: 6, isFrozen: false }], - // [[usdt_location], { symbol: 'USDT', name: 'USDC', decimals: 6, isFrozen: false }], - // [[dot_location], { symbol: 'DOT', name: 'DOT', decimals: 10, isFrozen: false }], - [[weth_location], { symbol: 'WETH', name: 'WETH', decimals: 18, isFrozen: false }], + [[weth_location], { symbol: 'Wrapped Ether', name: 'WETH', decimals: 18, isFrozen: false }], ], }, } as const; diff --git a/integration-tests/chopsticks/overrides/polkadot-hub.ts b/integration-tests/chopsticks/overrides/polkadot-hub.ts index 0bfbd4348..66a718b5e 100644 --- a/integration-tests/chopsticks/overrides/polkadot-hub.ts +++ b/integration-tests/chopsticks/overrides/polkadot-hub.ts @@ -1,7 +1,6 @@ import { INITIAL_BALANCES } from '@/constants'; import { Accounts, Asset } from '@/types'; - const weth_location = { parents: 2, interior: { @@ -9,18 +8,18 @@ const weth_location = { { globalConsensus: { ethereum: { - chainId: 1n - } - } + chainId: 1n, + }, + }, }, { accountKey20: { - key: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" - } - } - ] - } -} + key: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + }, + }, + ], + }, +}; export const polkadot_hub_storage = { System: { @@ -53,11 +52,6 @@ export const polkadot_hub_storage = { ], }, ForeignAssets: { - Account: [ - [[weth_location, Accounts.ALICE], { balance: INITIAL_BALANCES.WETH }] - ], - Asset: [ - [[weth_location], { supply: INITIAL_BALANCES.WETH }] - ] - } + Account: [[[weth_location, Accounts.ALICE], { balance: INITIAL_BALANCES.WETH }]], + }, } as const; diff --git a/integration-tests/chopsticks/src/constants.ts b/integration-tests/chopsticks/src/constants.ts index 9c73d0b37..4c8b2f611 100644 --- a/integration-tests/chopsticks/src/constants.ts +++ b/integration-tests/chopsticks/src/constants.ts @@ -11,7 +11,7 @@ export const INITIAL_BALANCES = { export const TRANSFER_AMOUNTS = { TOKENS: 2n * 10n ** 6n, // e.g. 2 USDC NATIVE: 2n * 10n ** 10n, // e.g. 2 DOT - BRIDGED: 1n * 10n ** 10n, // e.g. 0.1 WETH + BRIDGED: 1n * 10n ** 17n, // e.g. 0.1 WETH } as const; export const DERIVE_PATHS = { diff --git a/integration-tests/chopsticks/src/managers/BaseManager.ts b/integration-tests/chopsticks/src/managers/BaseManager.ts index a01552486..7a1ebca39 100644 --- a/integration-tests/chopsticks/src/managers/BaseManager.ts +++ b/integration-tests/chopsticks/src/managers/BaseManager.ts @@ -1,12 +1,11 @@ import { DERIVE_PATHS } from '@/constants'; -import { - type Accounts, - type Asset, - AssetLocation, - type AssetSourceRelation, - type ChainClient, - type ChainToDefinition, - type Chains, +import type { + Accounts, + Asset, + AssetSourceRelation, + ChainClient, + ChainToDefinition, + Chains, } from '@/types'; import { sr25519CreateDerive } from '@polkadot-labs/hdkd'; import { DEV_PHRASE, entropyToMiniSecret, mnemonicToEntropy } from '@polkadot-labs/hdkd-helpers'; @@ -75,26 +74,9 @@ export abstract class BaseChainManager { return events[0]?.payload.actual_fee || 0n; } - // Make sure to override this in the other managers abstract getAssetSourceRelation(asset: Asset): AssetSourceRelation; - async getAssetBalanceOf(account: Accounts, asset: Asset): Promise { - const chain = this.getChainType(); - const api = this.getApi(chain); - const asset_source_relation = this.getAssetSourceRelation(asset); - const asset_location = AssetLocation(asset, asset_source_relation); - const account_balances_result = await api.apis.FungiblesApi.query_account_balances(account); - - if (account_balances_result.success === true && account_balances_result.value.type === 'V4') { - const assets = account_balances_result.value.value; - for (const asset of assets) { - if (asset.id === asset_location && asset.fun.type === 'Fungible') { - return asset.fun.value; - } - } - } - return 0n; - } + abstract getAssetBalanceOf(account: Accounts, asset: Asset): Promise; // @ts-expect-error - TODO: Not sure which is the correct type for this abstract getXcmPallet(); diff --git a/integration-tests/chopsticks/src/managers/PolimecManager.ts b/integration-tests/chopsticks/src/managers/PolimecManager.ts index fc75e9912..210dd3eef 100644 --- a/integration-tests/chopsticks/src/managers/PolimecManager.ts +++ b/integration-tests/chopsticks/src/managers/PolimecManager.ts @@ -1,10 +1,9 @@ import { type Accounts, Asset, AssetLocation, AssetSourceRelation, Chains } from '@/types'; +import { flatObject } from '@/utils.ts'; import { polimec } from '@polkadot-api/descriptors'; -import { isEqual } from 'lodash'; import { createClient } from 'polkadot-api'; import { getWsProvider } from 'polkadot-api/ws-provider/web'; import { BaseChainManager } from './BaseManager'; -import { normalizeForComparison } from '@/utils.ts'; export class PolimecManager extends BaseChainManager { connect() { @@ -55,25 +54,15 @@ export class PolimecManager extends BaseChainManager { const asset_source_relation = this.getAssetSourceRelation(asset); const asset_location = AssetLocation(asset, asset_source_relation).value; const account_balances_result = await api.apis.FungiblesApi.query_account_balances(account); - console.log('Requested asset location in PolimecManager'); - console.dir(asset_location, { depth: null, colors: true }); - console.log('\n\n'); if (account_balances_result.success === true && account_balances_result.value.type === 'V4') { const assets = account_balances_result.value.value; for (const asset of assets) { - if (Bun.deepEquals(normalizeForComparison(asset.id), normalizeForComparison(asset_location))) { - console.log('Found asset. Balance is: ', asset.fun.value); - console.dir(asset, { depth: null, colors: true }); - console.log('\n\n'); - return asset.fun.value; + if (Bun.deepEquals(flatObject(asset.id), flatObject(asset_location))) { + return asset.fun.value as bigint; } - console.log('Not it chief: \n'); - console.dir(asset, { depth: null, colors: true }); - console.log('\n\n'); } } - console.log('Asset not found'); - console.log('\n\n'); + console.log('Asset not found using query_account_balances Runtime API'); return 0n; } diff --git a/integration-tests/chopsticks/src/managers/PolkadotHubManager.ts b/integration-tests/chopsticks/src/managers/PolkadotHubManager.ts index f74d522a8..c72b29cbb 100644 --- a/integration-tests/chopsticks/src/managers/PolkadotHubManager.ts +++ b/integration-tests/chopsticks/src/managers/PolkadotHubManager.ts @@ -1,11 +1,9 @@ import { type Accounts, Asset, AssetLocation, AssetSourceRelation, Chains } from '@/types'; +import { flatObject } from '@/utils.ts'; import { pah } from '@polkadot-api/descriptors'; import { createClient } from 'polkadot-api'; import { getWsProvider } from 'polkadot-api/ws-provider/web'; import { BaseChainManager } from './BaseManager'; -import { normalizeForComparison } from '@/utils.ts'; - - export class PolkadotHubManager extends BaseChainManager { connect() { @@ -59,23 +57,12 @@ export class PolkadotHubManager extends BaseChainManager { if (account_balances_result.success === true && account_balances_result.value.type === 'V4') { const assets = account_balances_result.value.value; for (const asset of assets) { - if ( - Bun.deepEquals(normalizeForComparison(asset.id), normalizeForComparison(asset_location)) - ) { - console.log('Found asset. Balance is: ', asset.fun.value); - console.dir(normalizeForComparison(asset), { depth: null, colors: true }); - console.log('\n\n'); - return asset.fun.value; + if (Bun.deepEquals(flatObject(asset.id), flatObject(asset_location))) { + return asset.fun.value as bigint; } - console.log('Not it chief: \n'); - console.dir(normalizeForComparison(asset.id), { depth: null, colors: true }); - - console.log('\n\n'); - } } - console.log('Asset not found'); - console.log('\n\n'); + console.log('Asset not found using query_account_balances Runtime API'); return 0n; } diff --git a/integration-tests/chopsticks/src/managers/PolkadotManager.ts b/integration-tests/chopsticks/src/managers/PolkadotManager.ts index e1b9bead6..bc44e1b4d 100644 --- a/integration-tests/chopsticks/src/managers/PolkadotManager.ts +++ b/integration-tests/chopsticks/src/managers/PolkadotManager.ts @@ -1,4 +1,3 @@ -import { TRANSFER_AMOUNTS } from '@/constants.ts'; import { type Accounts, Asset, AssetSourceRelation, Chains } from '@/types'; import { polkadot } from '@polkadot-api/descriptors'; import { createClient } from 'polkadot-api'; diff --git a/integration-tests/chopsticks/src/setup.ts b/integration-tests/chopsticks/src/setup.ts index 7a6e50f2f..66bd2e8ed 100644 --- a/integration-tests/chopsticks/src/setup.ts +++ b/integration-tests/chopsticks/src/setup.ts @@ -1,3 +1,4 @@ +import { expect } from 'bun:test'; import { setupWithServer } from '@acala-network/chopsticks'; import { type Blockchain, @@ -47,7 +48,12 @@ export class ChainSetup { console.log('✅ HRMP channels created'); // Needed to execute storage migrations within the new WASM before running tests. - await this.polimec?.newBlock(); + const head = this.polimec.head; + console.log(`✅ Polimec chain is at block ${head.number}`); + console.log('✅ Running storage migrations...'); + const new_block = await this.polimec?.newBlock(); + console.log(`✅ Polimec chain is at block ${new_block.number}`); + expect(new_block.number === head.number + 1, 'Block number should be incremented by 1'); } async cleanup() { @@ -83,7 +89,6 @@ export class ChainSetup { 'wasm-override': POLIMEC_WASM, 'import-storage': polimec_storage, 'build-block-mode': BuildBlockMode.Instant, - 'runtime-log-level': 5, }); } @@ -93,7 +98,6 @@ export class ChainSetup { port: 8001, 'import-storage': polkadot_hub_storage, 'build-block-mode': BuildBlockMode.Instant, - 'runtime-log-level': 5, }); } diff --git a/integration-tests/chopsticks/src/tests/polimec.test.ts b/integration-tests/chopsticks/src/tests/polimec.test.ts index b053e10b7..51df14669 100644 --- a/integration-tests/chopsticks/src/tests/polimec.test.ts +++ b/integration-tests/chopsticks/src/tests/polimec.test.ts @@ -1,62 +1,62 @@ -import { afterAll, beforeAll, beforeEach, describe, test } from 'bun:test'; -import { TRANSFER_AMOUNTS } from '@/constants'; -import { createChainManager } from '@/managers/Factory'; -import { polimec_storage } from '@/polimec'; -import { ChainSetup } from '@/setup'; -import { PolimecToHubTransfer } from '@/transfers/PolimecToHub'; -import { Accounts, Asset, AssetSourceRelation, Chains } from '@/types'; +// import { afterAll, beforeAll, beforeEach, describe, test } from 'bun:test'; +// import { TRANSFER_AMOUNTS } from '@/constants'; +// import { createChainManager } from '@/managers/Factory'; +// import { polimec_storage } from '@/polimec'; +// import { ChainSetup } from '@/setup'; +// import { PolimecToHubTransfer } from '@/transfers/PolimecToHub'; +// import { Accounts, Asset, AssetSourceRelation, Chains } from '@/types'; -describe('Polimec -> Hub Transfer Tests', () => { - const sourceManager = createChainManager(Chains.Polimec); - const destManager = createChainManager(Chains.PolkadotHub); - const transferTest = new PolimecToHubTransfer(sourceManager, destManager); - const chainSetup = new ChainSetup(); +// describe('Polimec -> Hub Transfer Tests', () => { +// const sourceManager = createChainManager(Chains.Polimec); +// const destManager = createChainManager(Chains.PolkadotHub); +// const transferTest = new PolimecToHubTransfer(sourceManager, destManager); +// const chainSetup = new ChainSetup(); - beforeAll(async () => await chainSetup.initialize(polimec_storage)); - beforeEach(() => { - sourceManager.connect(); - destManager.connect(); - }); - afterAll(async () => await chainSetup.cleanup()); +// beforeAll(async () => await chainSetup.initialize(polimec_storage)); +// beforeEach(() => { +// sourceManager.connect(); +// destManager.connect(); +// }); +// afterAll(async () => await chainSetup.cleanup()); - async function getBalance(account: Accounts, asset: Asset) { - return await sourceManager.getAssetBalanceOf(account, asset); - } - test('Balance query', () => getBalance(Accounts.BOB, Asset.USDT), { timeout: 250000000 }); +// async function getBalance(account: Accounts, asset: Asset) { +// return await sourceManager.getAssetBalanceOf(account, asset); +// } +// test('Balance query', () => getBalance(Accounts.BOB, Asset.USDT), { timeout: 250000000 }); - test( - 'Send USDC to Hub', - () => - transferTest.testTransfer({ - amount: TRANSFER_AMOUNTS.TOKENS, - account: Accounts.BOB, - asset: Asset.USDC, - assetSourceRelation: AssetSourceRelation.Sibling, - }), - { timeout: 25000 }, - ); +// test( +// 'Send USDC to Hub', +// () => +// transferTest.testTransfer({ +// amount: TRANSFER_AMOUNTS.TOKENS, +// account: Accounts.BOB, +// asset: Asset.USDC, +// assetSourceRelation: AssetSourceRelation.Sibling, +// }), +// { timeout: 25000 }, +// ); - test( - 'Send USDT to Hub', - () => - transferTest.testTransfer({ - amount: TRANSFER_AMOUNTS.TOKENS, - account: Accounts.BOB, - asset: Asset.USDT, - assetSourceRelation: AssetSourceRelation.Sibling, - }), - { timeout: 25000 }, - ); +// test( +// 'Send USDT to Hub', +// () => +// transferTest.testTransfer({ +// amount: TRANSFER_AMOUNTS.TOKENS, +// account: Accounts.BOB, +// asset: Asset.USDT, +// assetSourceRelation: AssetSourceRelation.Sibling, +// }), +// { timeout: 25000 }, +// ); - test( - 'Send DOT to Hub', - () => - transferTest.testTransfer({ - amount: TRANSFER_AMOUNTS.NATIVE, - account: Accounts.BOB, - asset: Asset.DOT, - assetSourceRelation: AssetSourceRelation.Parent, - }), - { timeout: 25000 }, - ); -}); +// test( +// 'Send DOT to Hub', +// () => +// transferTest.testTransfer({ +// amount: TRANSFER_AMOUNTS.NATIVE, +// account: Accounts.BOB, +// asset: Asset.DOT, +// assetSourceRelation: AssetSourceRelation.Parent, +// }), +// { timeout: 25000 }, +// ); +// }); diff --git a/integration-tests/chopsticks/src/tests/polkadot.test.ts b/integration-tests/chopsticks/src/tests/polkadot.test.ts index 699ec86ad..72f99e8c1 100644 --- a/integration-tests/chopsticks/src/tests/polkadot.test.ts +++ b/integration-tests/chopsticks/src/tests/polkadot.test.ts @@ -1,32 +1,32 @@ -import { afterAll, beforeAll, beforeEach, describe, test } from 'bun:test'; -import { TRANSFER_AMOUNTS } from '@/constants'; -import { createChainManager } from '@/managers/Factory'; -import { ChainSetup } from '@/setup'; -import { PolkadotToPolimecTransfer } from '@/transfers/PolkadotToPolimec'; -import { Accounts, Asset, Chains } from '@/types'; +// import { afterAll, beforeAll, beforeEach, describe, test } from 'bun:test'; +// import { TRANSFER_AMOUNTS } from '@/constants'; +// import { createChainManager } from '@/managers/Factory'; +// import { ChainSetup } from '@/setup'; +// import { PolkadotToPolimecTransfer } from '@/transfers/PolkadotToPolimec'; +// import { Accounts, Asset, Chains } from '@/types'; -describe('Polkadot -> Polimec Transfer Tests', () => { - const chainSetup = new ChainSetup(); +// describe('Polkadot -> Polimec Transfer Tests', () => { +// const chainSetup = new ChainSetup(); - const sourceManager = createChainManager(Chains.Polkadot); - const destManager = createChainManager(Chains.Polimec); - const transferTest = new PolkadotToPolimecTransfer(sourceManager, destManager); +// const sourceManager = createChainManager(Chains.Polkadot); +// const destManager = createChainManager(Chains.Polimec); +// const transferTest = new PolkadotToPolimecTransfer(sourceManager, destManager); - beforeAll(async () => await chainSetup.initialize()); - beforeEach(() => { - sourceManager.connect(); - destManager.connect(); - }); - afterAll(async () => await chainSetup.cleanup()); +// beforeAll(async () => await chainSetup.initialize()); +// beforeEach(() => { +// sourceManager.connect(); +// destManager.connect(); +// }); +// afterAll(async () => await chainSetup.cleanup()); - test( - 'Send DOT to Polimec', - () => - transferTest.testTransfer({ - amount: TRANSFER_AMOUNTS.NATIVE, - account: Accounts.ALICE, - asset: Asset.DOT, - }), - { timeout: 25000 }, - ); -}); +// test( +// 'Send DOT to Polimec', +// () => +// transferTest.testTransfer({ +// amount: TRANSFER_AMOUNTS.NATIVE, +// account: Accounts.ALICE, +// asset: Asset.DOT, +// }), +// { timeout: 25000 }, +// ); +// }); diff --git a/integration-tests/chopsticks/src/transfers/BaseTransfer.ts b/integration-tests/chopsticks/src/transfers/BaseTransfer.ts index 6219e53a9..311a96300 100644 --- a/integration-tests/chopsticks/src/transfers/BaseTransfer.ts +++ b/integration-tests/chopsticks/src/transfers/BaseTransfer.ts @@ -1,13 +1,13 @@ import { expect } from 'bun:test'; import type { BaseChainManager } from '@/managers/BaseManager'; -import { - type Accounts, - type Asset, - type AssetSourceRelation, - type BalanceCheck, - Chains, - type TransferResult, +import type { + Accounts, + Asset, + AssetSourceRelation, + BalanceCheck, + TransferResult, } from '@/types'; +import { sleep } from 'bun'; export interface TransferOptions { account: Accounts; @@ -18,7 +18,7 @@ export abstract class BaseTransferTest { constructor( protected sourceManager: BaseChainManager, protected destManager: BaseChainManager, - ) {} + ) { } abstract executeTransfer(options: TransferOptions): Promise; abstract getBalances(options: TransferOptions): Promise<{ asset_balances: BalanceCheck[] }>; @@ -26,34 +26,30 @@ export abstract class BaseTransferTest { initialBalances: BalanceCheck[], finalBalances: BalanceCheck[], options: TransferOptions, - ): Promise; + ): void; async testTransfer(options: TransferOptions) { const { asset_balances: initialBalances } = await this.getBalances(options); + if (options.assets[0][1] > initialBalances[0].source) { + throw new Error(`Insufficient balance on Source chain for asset: ${options.assets[0][0]}`); + } const blockNumbers = await this.executeTransfer(options); - await this.waitForBlocks(blockNumbers); + await this.waitForBlocks(blockNumbers.destBlock); await this.verifyExecution(); const { asset_balances: finalBalances } = await this.getBalances(options); - await this.verifyFinalBalances(initialBalances, finalBalances, options); + this.verifyFinalBalances(initialBalances, finalBalances, options); } - protected async waitForBlocks({ sourceBlock, destBlock }: TransferResult) { - await Promise.all([ - this.sourceManager.waitForNextBlock(sourceBlock), - this.destManager.waitForNextBlock(destBlock), - ]); + protected async waitForBlocks(destBlockNumber: number) { + await sleep(2000); } protected async verifyExecution() { const events = await this.destManager.getMessageQueueEvents(); - const v = await this.destManager - .getApi(Chains.Polimec) - .event.MessageQueue.ProcessingFailed.pull(); - console.log('MsgQ Events:', v); expect(events).not.toBeEmpty(); expect(events).toBeArray(); expect(events).toHaveLength(1); expect(events[0].payload.success).toBeTrue(); } -} +} \ No newline at end of file diff --git a/integration-tests/chopsticks/src/transfers/HubToPolimec.ts b/integration-tests/chopsticks/src/transfers/HubToPolimec.ts index cc26cef4b..7e8d9c105 100644 --- a/integration-tests/chopsticks/src/transfers/HubToPolimec.ts +++ b/integration-tests/chopsticks/src/transfers/HubToPolimec.ts @@ -2,28 +2,21 @@ import { expect } from 'bun:test'; import type { PolimecManager } from '@/managers/PolimecManager'; import type { PolkadotHubManager } from '@/managers/PolkadotHubManager'; import { - type BalanceCheck, + Asset, + AssetSourceRelation, Chains, + ParaId, type PolimecBalanceCheck, getVersionedAssets, - AssetSourceRelation, - Asset, } from '@/types'; import { createTransferData, unwrap } from '@/utils'; import { DispatchRawOrigin, - XcmPalletOrigin, XcmVersionedAssetId, - XcmVersionedLocation, + type XcmVersionedLocation, type XcmVersionedXcm, } from '@polkadot-api/descriptors'; -import type { - I4c0s5cioidn76, - I5gi8h3e5lkbeq, - I47tkk5e5nm6g7, -} from '@polkadot-api/descriptors/dist/common-types'; - import { BaseTransferTest, type TransferOptions } from './BaseTransfer'; export class HubToPolimecTransfer extends BaseTransferTest { @@ -52,8 +45,7 @@ export class HubToPolimecTransfer extends BaseTransferTest { const transfer = api.tx.PolkadotXcm.transfer_assets(data); const res = await transfer.signAndSubmit(this.sourceManager.getSigner(account)); - console.log("Transfer result"); - console.dir(res, { depth: null, colors: true }); + console.log('Extrinsic result: ', res.ok); expect(res.ok).toBeTrue(); return { sourceBlock, destBlock }; @@ -85,20 +77,13 @@ export class HubToPolimecTransfer extends BaseTransferTest { const source_xcm_asset_fee_amount = await this.sourceManager.getXcmFee(); const dest_xcm_asset_fee_amount = await this.calculatePolimecXcmFee(transferOptions); - console.log('Native extrinsic fee amount: ', native_extrinsic_fee_amount); - console.log('Source xcm fee amount: ', source_xcm_asset_fee_amount); - console.log('Dest xcm fee amount: ', dest_xcm_asset_fee_amount); - const fee_asset = transferOptions.assets[0][0]; for (let i = 0; i < transferOptions.assets.length; i++) { const initialBalances = assetInitialBalances[i]; const finalBalances = assetFinalBalances[i]; const send_amount = transferOptions.assets[i][1]; - console.log('Send amount: ', send_amount); - const asset = transferOptions.assets[i][0]; - console.log('Asset: ', asset); let expectedSourceBalanceSpent = send_amount; let expectedDestBalanceSpent = 0n; @@ -112,13 +97,7 @@ export class HubToPolimecTransfer extends BaseTransferTest { expectedTreasuryBalanceGained += dest_xcm_asset_fee_amount; } - console.log('Expected source balance spent: ', expectedSourceBalanceSpent); - console.log('Expected dest balance spent: ', expectedDestBalanceSpent); - console.log('Expected treasury balance gained: ', expectedTreasuryBalanceGained); - - expect(finalBalances.source).toBe( - initialBalances.source - expectedSourceBalanceSpent, - ); + expect(finalBalances.source).toBe(initialBalances.source - expectedSourceBalanceSpent); expect(finalBalances.destination).toBe( initialBalances.destination + send_amount - expectedDestBalanceSpent, ); @@ -132,8 +111,6 @@ export class HubToPolimecTransfer extends BaseTransferTest { const sourceApi = this.sourceManager.getApi(Chains.PolkadotHub); const destApi = this.destManager.getApi(Chains.Polimec); - - const versioned_assets = getVersionedAssets(transferOptions.assets); const transferData = createTransferData({ toChain: Chains.Polimec, @@ -142,11 +119,11 @@ export class HubToPolimecTransfer extends BaseTransferTest { }); let remoteFeeAssetId: XcmVersionedAssetId; - let lastAsset = unwrap(transferOptions.assets.at(0)); + const lastAsset = unwrap(transferOptions.assets.at(0)); if (lastAsset[2] === AssetSourceRelation.Self) { lastAsset[2] = AssetSourceRelation.Sibling; } - let versioned_asset = getVersionedAssets([lastAsset]); + const versioned_asset = getVersionedAssets([lastAsset]); if (versioned_asset.type === 'V4') { remoteFeeAssetId = XcmVersionedAssetId.V4(unwrap(versioned_asset.value.at(0)).id); } else { @@ -158,7 +135,7 @@ export class HubToPolimecTransfer extends BaseTransferTest { { type: 'PolkadotXcm', value: { type: 'transfer_assets', value: transferData } }, ); - let forwardedXcms: I47tkk5e5nm6g7; + let forwardedXcms: [XcmVersionedLocation, XcmVersionedXcm[]][] = []; if (localDryRunResult.success && localDryRunResult.value.forwarded_xcms) { forwardedXcms = localDryRunResult.value.forwarded_xcms; } else { @@ -171,31 +148,19 @@ export class HubToPolimecTransfer extends BaseTransferTest { location.value.parents === 1 && location.value.interior.type === 'X1' && location.value.interior.value.type === 'Parachain' && - location.value.interior.value.value === 3344, // Polimec's ParaID. + location.value.interior.value.value === ParaId[Chains.Polimec], ); if (!xcmsToPolimec) { throw new Error('Could not find xcm to polimec'); } const messages = xcmsToPolimec[1]; const remoteXcm = messages[0]; - - console.log('Remote XCM:'); - console.dir(remoteXcm, { depth: null, colors: true }); - const assets = await destApi.apis.XcmPaymentApi.query_acceptable_payment_assets(4); - console.log("Acceptable payment assets") - console.dir(assets, { depth: null, colors: true }); const remoteXcmWeightResult = await destApi.apis.XcmPaymentApi.query_xcm_weight(remoteXcm); - console.log('XCM Weight:'); - console.dir(remoteXcmWeightResult, { depth: null, colors: true }); if (remoteXcmWeightResult.success) { - console.log("fee asset id"); - console.dir(remoteFeeAssetId, { depth: null, colors: true }); const remoteExecutionFeesResult = await destApi.apis.XcmPaymentApi.query_weight_to_asset_fee( remoteXcmWeightResult.value, remoteFeeAssetId, ); - console.log('remoteExecutionFeesResult'); - console.dir(remoteExecutionFeesResult, { depth: null, colors: true }); if (remoteExecutionFeesResult.success) { destinationExecutionFee = remoteExecutionFeesResult.value; } else { diff --git a/integration-tests/chopsticks/src/transfers/PolimecToHub.ts b/integration-tests/chopsticks/src/transfers/PolimecToHub.ts index c4376cd23..166fe50e5 100644 --- a/integration-tests/chopsticks/src/transfers/PolimecToHub.ts +++ b/integration-tests/chopsticks/src/transfers/PolimecToHub.ts @@ -2,17 +2,11 @@ import { expect } from 'bun:test'; import { INITIAL_BALANCES } from '@/constants'; import type { PolimecManager } from '@/managers/PolimecManager'; import type { PolkadotHubManager } from '@/managers/PolkadotHubManager'; -import { - Asset, - type AssetSourceRelation, - type BalanceCheck, - Chains, - getVersionedAssets, -} from '@/types'; +import { Asset, type BalanceCheck, Chains, getVersionedAssets } from '@/types'; import { createTransferData } from '@/utils'; -import { type BaseTransferOptions, BaseTransferTest } from './BaseTransfer'; +import { BaseTransferTest, type TransferOptions } from './BaseTransfer'; -export class PolimecToHubTransfer extends BaseTransferTest { +export class PolimecToHubTransfer extends BaseTransferTest { constructor( protected override sourceManager: PolimecManager, protected override destManager: PolkadotHubManager, @@ -20,7 +14,7 @@ export class PolimecToHubTransfer extends BaseTransferTest super(sourceManager, destManager); } - async executeTransfer({ account, assets }: BaseTransferOptions) { + async executeTransfer({ account, assets }: TransferOptions) { const [sourceBlock, destBlock] = await Promise.all([ this.sourceManager.getBlockNumber(), this.destManager.getBlockNumber(), @@ -42,34 +36,35 @@ export class PolimecToHubTransfer extends BaseTransferTest return { sourceBlock, destBlock }; } - async getBalances({ - account, - asset, - }: Omit): Promise<{ balances: BalanceCheck }> { - return { - balances: { - source: await this.sourceManager.getAssetBalanceOf(account, asset), - destination: await this.destManager.getAssetBalanceOf(account, asset), - }, - }; + async getBalances(options: TransferOptions): Promise<{ asset_balances: BalanceCheck[] }> { + const source = await this.sourceManager.getAssetBalanceOf( + options.account, + options.assets[0][0], + ); + const destination = await this.destManager.getAssetBalanceOf( + options.account, + options.assets[0][0], + ); + return { asset_balances: [{ source, destination }] }; } - async verifyFinalBalances( - initialBalances: BalanceCheck, - finalBalances: BalanceCheck, - { asset }: PolimecTransferOptions, + verifyFinalBalances( + initialBalances: BalanceCheck[], + finalBalances: BalanceCheck[], + options: TransferOptions, ) { // TODO: At the moment we exclude fees from the balance check since the PAPI team is wotking on some utilies to calculate fees. const initialBalance = - asset === Asset.DOT + options.assets[0][0] === Asset.DOT ? INITIAL_BALANCES.DOT - : asset === Asset.USDT + : options.assets[0][0] === Asset.USDT ? INITIAL_BALANCES.USDT : INITIAL_BALANCES.USDC; - - expect(initialBalances.destination).toBe(0n); - expect(initialBalances.source).toBe(initialBalance); - expect(finalBalances.source).toBeLessThan(initialBalances.source); - expect(finalBalances.destination).toBeGreaterThan(initialBalances.destination); + for (let i = 0; i < options.assets.length; i++) { + expect(initialBalances[i].destination).toBe(0n); + expect(initialBalances[i].source).toBe(initialBalance); + expect(finalBalances[i].source).toBeLessThan(initialBalances[i].source); + expect(finalBalances[i].destination).toBeGreaterThan(initialBalances[i].destination); + } } -} +} \ No newline at end of file diff --git a/integration-tests/chopsticks/src/transfers/PolkadotToPolimec.ts b/integration-tests/chopsticks/src/transfers/PolkadotToPolimec.ts index 54bbff0f4..b36fd31ba 100644 --- a/integration-tests/chopsticks/src/transfers/PolkadotToPolimec.ts +++ b/integration-tests/chopsticks/src/transfers/PolkadotToPolimec.ts @@ -1,11 +1,11 @@ import { expect } from 'bun:test'; import type { PolimecManager } from '@/managers/PolimecManager'; import type { PolkadotManager } from '@/managers/PolkadotManager'; -import { Asset, Chains, type PolimecBalanceCheck } from '@/types'; +import { Asset, type BalanceCheck, Chains, type PolimecBalanceCheck } from '@/types'; import { createDotMultiHopTransferData } from '@/utils'; -import { type BaseTransferOptions, BaseTransferTest } from './BaseTransfer'; +import { BaseTransferTest, type TransferOptions } from './BaseTransfer'; -export class PolkadotToPolimecTransfer extends BaseTransferTest { +export class PolkadotToPolimecTransfer extends BaseTransferTest { constructor( protected override sourceManager: PolkadotManager, protected override destManager: PolimecManager, @@ -13,7 +13,7 @@ export class PolkadotToPolimecTransfer extends BaseTransferTest): Promise<{ balances: PolimecBalanceCheck }> { - const treasuryAccount = this.destManager.getTreasuryAccount(); - return { - balances: { - source: await this.sourceManager.getAssetBalanceOf(account, Asset.DOT), - destination: await this.destManager.getAssetBalanceOf(account, Asset.DOT), - treasury: await this.destManager.getAssetBalanceOf(treasuryAccount, Asset.DOT), - }, - }; + async getBalances(options: TransferOptions): Promise<{ asset_balances: PolimecBalanceCheck[] }> { + throw new Error('Method not implemented.'); } async verifyFinalBalances( - initialBalances: PolimecBalanceCheck, - finalBalances: PolimecBalanceCheck, - ) { - // TODO: At the moment we exclude fees from the balance check since the PAPI team is wotking on some utilies to calculate fees. - expect(initialBalances.destination).toBe(0n); - expect(finalBalances.source).toBeLessThan(initialBalances.source); - expect(finalBalances.destination).toBeGreaterThan(initialBalances.destination); - expect(finalBalances.treasury).toBeGreaterThan(initialBalances.treasury); + initialBalances: PolimecBalanceCheck[], + finalBalances: PolimecBalanceCheck[], + options: TransferOptions, + ): Promise { + throw new Error('Method not implemented.'); } -} + + // async getBalances({ + // account, + // }: Omit): Promise<{ balances: PolimecBalanceCheck }> { + // const treasuryAccount = this.destManager.getTreasuryAccount(); + // return { + // balances: { + // source: await this.sourceManager.getAssetBalanceOf(account, Asset.DOT), + // destination: await this.destManager.getAssetBalanceOf(account, Asset.DOT), + // treasury: await this.destManager.getAssetBalanceOf(treasuryAccount, Asset.DOT), + // }, + // }; + // } + + // async verifyFinalBalances( + // initialBalances: PolimecBalanceCheck, + // finalBalances: PolimecBalanceCheck, + // ) { + // // TODO: At the moment we exclude fees from the balance check since the PAPI team is wotking on some utilies to calculate fees. + // expect(initialBalances.destination).toBe(0n); + // expect(finalBalances.source).toBeLessThan(initialBalances.source); + // expect(finalBalances.destination).toBeGreaterThan(initialBalances.destination); + // expect(finalBalances.treasury).toBeGreaterThan(initialBalances.treasury); + // } +} \ No newline at end of file diff --git a/integration-tests/chopsticks/src/types.ts b/integration-tests/chopsticks/src/types.ts index 6c75fed1e..4a534e7f4 100644 --- a/integration-tests/chopsticks/src/types.ts +++ b/integration-tests/chopsticks/src/types.ts @@ -2,7 +2,6 @@ import { XcmV3Junction, XcmV3JunctionNetworkId, XcmV3Junctions, - XcmV3MultiassetAssetId, XcmV3MultiassetFungibility, XcmVersionedAssets, XcmVersionedLocation, @@ -10,9 +9,8 @@ import { type polimec, type polkadot, } from '@polkadot-api/descriptors'; -import type { Ia5l7mu5a6v49o } from '@polkadot-api/descriptors/dist/common-types'; -import { hexToU8a } from '@polkadot/util'; import { FixedSizeBinary, type PolkadotClient, type TypedApi } from 'polkadot-api'; + type Polimec = typeof polimec; type PolkadotHub = typeof pah; type Polkadot = typeof polkadot; @@ -22,10 +20,12 @@ export enum Chains { PolkadotHub = 'ws://localhost:8001', Polkadot = 'ws://localhost:8002', } + export type ChainClient = { api: TypedApi; client: PolkadotClient; }; + export const ParaId = { [Chains.Polimec]: 3344, [Chains.PolkadotHub]: 1000, @@ -121,7 +121,7 @@ export function NativeAssetLocation( } return XcmVersionedLocation.V4({ parents: 1, - interior: XcmV3Junctions.X1([XcmV3Junction.Parachain(paraId)]), + interior: XcmV3Junctions.X1(XcmV3Junction.Parachain(paraId)), }); case AssetSourceRelation.Self: return XcmVersionedLocation.V4({ @@ -170,11 +170,18 @@ export function AssetLocation( export function getVersionedAssets( assets: [Asset, bigint, AssetSourceRelation][], ): XcmVersionedAssets { - const final_assets: Ia5l7mu5a6v49o[] = []; + const final_assets: { + id: { parents: number; interior: XcmV3Junctions }; + fun: XcmV3MultiassetFungibility; + }[] = []; for (const [asset, amount, asset_source_relation] of assets) { const location = AssetLocation(asset, asset_source_relation); + const id = { + parents: location.value.parents, + interior: location.value.interior as XcmV3Junctions, // We assume that this is not an XCM v2 MultiLocation. + }; final_assets.push({ - id: location.value, + id, fun: XcmV3MultiassetFungibility.Fungible(amount), }); } diff --git a/integration-tests/chopsticks/src/utils.ts b/integration-tests/chopsticks/src/utils.ts index 850dd08d8..5e7b9d16d 100644 --- a/integration-tests/chopsticks/src/utils.ts +++ b/integration-tests/chopsticks/src/utils.ts @@ -94,7 +94,7 @@ export const createDotMultiHopTransferData = (amount: bigint) => { return { dest, - assets: getVersionedAssets([[Asset.DOT, amount]], AssetSourceRelation.Self), + assets: getVersionedAssets([[Asset.DOT, amount, AssetSourceRelation.Self]]), assets_transfer_type: Enum('Teleport'), remote_fees_id: XcmVersionedAssetId.V3( XcmV3MultiassetAssetId.Concrete({ @@ -115,26 +115,22 @@ export function unwrap(value: T | undefined, errorMessage = 'Value is undefin return value; } -export function normalizeForComparison(obj: any): any { +export function flatObject(obj: unknown): unknown { if (obj === null || obj === undefined) { return obj; } - - if (obj instanceof Object && 'asHex' in obj) { - return obj.asHex(); + if (obj instanceof Object && typeof (obj as { asHex?: unknown }).asHex === 'function') { + return (obj as { asHex: () => unknown }).asHex(); } - if (typeof obj === 'object') { if (Array.isArray(obj)) { - return obj.map(normalizeForComparison); + return obj.map(flatObject); } - - const normalized: any = {}; + const normalized: Record = {}; for (const [key, value] of Object.entries(obj)) { - normalized[key] = normalizeForComparison(value); + normalized[key] = flatObject(value); } return normalized; } - return obj; -} \ No newline at end of file +}