diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index f9d3bc05e0..5a3bfe64cf 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -1261,6 +1261,12 @@ pub mod pallet { VersionNotifyTargets::::remove(XCM_VERSION, LatestVersionedMultiLocation(dest)); Ok(()) } + + /// Return true if a location is subscribed to XCM version changes. + fn is_subscribed(dest: &MultiLocation) -> bool { + let versioned_dest = LatestVersionedMultiLocation(dest); + VersionNotifyTargets::::contains_key(XCM_VERSION, versioned_dest) + } } impl DropAssets for Pallet { diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index 72bb8b61c0..7d94312a46 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -25,6 +25,9 @@ mod mock; #[cfg(test)] mod tests; +#[cfg(feature = "std")] +pub mod test_utils; + mod location_conversion; pub use location_conversion::{ Account32Hash, AccountId32Aliases, AccountKey20Aliases, ChildParachainConvertsVia, diff --git a/polkadot/xcm/xcm-builder/src/mock.rs b/polkadot/xcm/xcm-builder/src/mock.rs index 4efc02b7e1..131d09f71e 100644 --- a/polkadot/xcm/xcm-builder/src/mock.rs +++ b/polkadot/xcm/xcm-builder/src/mock.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use crate::barriers::AllowSubscriptionsFrom; +use crate::{barriers::AllowSubscriptionsFrom, test_utils::*}; pub use crate::{ AllowKnownQueryResponses, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, FixedRateOfFungible, FixedWeightBounds, LocationInverter, TakeWeightCredit, @@ -36,7 +36,6 @@ pub use sp_std::{ marker::PhantomData, }; pub use xcm::latest::prelude::*; -use xcm_executor::traits::{ClaimAssets, DropAssets, VersionChangeNotifier}; pub use xcm_executor::{ traits::{ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset}, Assets, Config, @@ -273,57 +272,6 @@ pub type TestBarrier = ( AllowSubscriptionsFrom>, ); -parameter_types! { - pub static TrappedAssets: Vec<(MultiLocation, MultiAssets)> = vec![]; -} - -pub struct TestAssetTrap; - -impl DropAssets for TestAssetTrap { - fn drop_assets(origin: &MultiLocation, assets: Assets) -> Weight { - let mut t: Vec<(MultiLocation, MultiAssets)> = TrappedAssets::get(); - t.push((origin.clone(), assets.into())); - TrappedAssets::set(t); - 5 - } -} - -impl ClaimAssets for TestAssetTrap { - fn claim_assets(origin: &MultiLocation, ticket: &MultiLocation, what: &MultiAssets) -> bool { - let mut t: Vec<(MultiLocation, MultiAssets)> = TrappedAssets::get(); - if let (0, X1(GeneralIndex(i))) = (ticket.parents, &ticket.interior) { - if let Some((l, a)) = t.get(*i as usize) { - if l == origin && a == what { - t.swap_remove(*i as usize); - TrappedAssets::set(t); - return true - } - } - } - false - } -} - -parameter_types! { - pub static SubscriptionRequests: Vec<(MultiLocation, Option<(QueryId, u64)>)> = vec![]; -} -pub struct TestSubscriptionService; - -impl VersionChangeNotifier for TestSubscriptionService { - fn start(location: &MultiLocation, query_id: QueryId, max_weight: u64) -> XcmResult { - let mut r = SubscriptionRequests::get(); - r.push((location.clone(), Some((query_id, max_weight)))); - SubscriptionRequests::set(r); - Ok(()) - } - fn stop(location: &MultiLocation) -> XcmResult { - let mut r = SubscriptionRequests::get(); - r.push((location.clone(), None)); - SubscriptionRequests::set(r); - Ok(()) - } -} - pub struct TestConfig; impl Config for TestConfig { type Call = TestCall; diff --git a/polkadot/xcm/xcm-builder/src/test_utils.rs b/polkadot/xcm/xcm-builder/src/test_utils.rs new file mode 100644 index 0000000000..30c436f467 --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/test_utils.rs @@ -0,0 +1,83 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +// Shared test utilities and implementations for the XCM Builder. + +use frame_support::{dispatch::Weight, parameter_types}; +use sp_std::vec::Vec; +pub use xcm::latest::prelude::*; +use xcm_executor::traits::{ClaimAssets, DropAssets, VersionChangeNotifier}; +pub use xcm_executor::{ + traits::{ConvertOrigin, FilterAssetLocation, InvertLocation, OnResponse, TransactAsset}, + Assets, Config, +}; + +parameter_types! { + pub static SubscriptionRequests: Vec<(MultiLocation, Option<(QueryId, u64)>)> = vec![]; +} + +pub struct TestSubscriptionService; + +impl VersionChangeNotifier for TestSubscriptionService { + fn start(location: &MultiLocation, query_id: QueryId, max_weight: u64) -> XcmResult { + let mut r = SubscriptionRequests::get(); + r.push((location.clone(), Some((query_id, max_weight)))); + SubscriptionRequests::set(r); + Ok(()) + } + fn stop(location: &MultiLocation) -> XcmResult { + let mut r = SubscriptionRequests::get(); + r.retain(|(l, _q)| l != location); + r.push((location.clone(), None)); + SubscriptionRequests::set(r); + Ok(()) + } + fn is_subscribed(location: &MultiLocation) -> bool { + let r = SubscriptionRequests::get(); + r.iter().any(|(l, q)| l == location && q.is_some()) + } +} + +parameter_types! { + pub static TrappedAssets: Vec<(MultiLocation, MultiAssets)> = vec![]; +} + +pub struct TestAssetTrap; + +impl DropAssets for TestAssetTrap { + fn drop_assets(origin: &MultiLocation, assets: Assets) -> Weight { + let mut t: Vec<(MultiLocation, MultiAssets)> = TrappedAssets::get(); + t.push((origin.clone(), assets.into())); + TrappedAssets::set(t); + 5 + } +} + +impl ClaimAssets for TestAssetTrap { + fn claim_assets(origin: &MultiLocation, ticket: &MultiLocation, what: &MultiAssets) -> bool { + let mut t: Vec<(MultiLocation, MultiAssets)> = TrappedAssets::get(); + if let (0, X1(GeneralIndex(i))) = (ticket.parents, &ticket.interior) { + if let Some((l, a)) = t.get(*i as usize) { + if l == origin && a == what { + t.swap_remove(*i as usize); + TrappedAssets::set(t); + return true + } + } + } + false + } +} diff --git a/polkadot/xcm/xcm-builder/src/tests.rs b/polkadot/xcm/xcm-builder/src/tests.rs index e3e5e24ca1..0e5c078604 100644 --- a/polkadot/xcm/xcm-builder/src/tests.rs +++ b/polkadot/xcm/xcm-builder/src/tests.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use super::{mock::*, *}; +use super::{mock::*, test_utils::*, *}; use frame_support::{assert_err, weights::constants::WEIGHT_PER_SECOND}; use xcm::latest::prelude::*; use xcm_executor::{traits::*, Config, XcmExecutor}; diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index bc6bb3246c..00cc84c4e5 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -139,26 +139,7 @@ impl ExecuteXcm for XcmExecutor { } } - vm.refund_surplus(); - drop(vm.trader); - - let mut weight_used = xcm_weight.saturating_sub(vm.total_surplus); - - if !vm.holding.is_empty() { - log::trace!(target: "xcm::execute_xcm_in_credit", "Trapping assets in holding register: {:?} (original_origin: {:?})", vm.holding, vm.original_origin); - let trap_weight = Config::AssetTrap::drop_assets(&vm.original_origin, vm.holding); - weight_used.saturating_accrue(trap_weight); - }; - - match vm.error { - None => Outcome::Complete(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::debug!(target: "xcm::execute_xcm_in_credit", "Execution errored at {:?}: {:?} (original_origin: {:?})", _i, e, vm.original_origin); - Outcome::Incomplete(weight_used, e) - }, - } + vm.post_execute(xcm_weight) } } @@ -228,6 +209,31 @@ impl XcmExecutor { result } + /// 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_execute(mut self, xcm_weight: Weight) -> Outcome { + 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::execute_xcm_in_credit", "Trapping assets in holding register: {:?} (original_origin: {:?})", self.holding, self.original_origin); + let trap_weight = Config::AssetTrap::drop_assets(&self.original_origin, self.holding); + weight_used.saturating_accrue(trap_weight); + }; + + match self.error { + None => Outcome::Complete(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::debug!(target: "xcm::execute_xcm_in_credit", "Execution errored at {:?}: {:?} (original_origin: {:?})", _i, e, self.original_origin); + Outcome::Incomplete(weight_used, e) + }, + } + } + /// 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![]); diff --git a/polkadot/xcm/xcm-executor/src/traits/on_response.rs b/polkadot/xcm/xcm-executor/src/traits/on_response.rs index a34d5264c0..c5431e0e8c 100644 --- a/polkadot/xcm/xcm-executor/src/traits/on_response.rs +++ b/polkadot/xcm/xcm-executor/src/traits/on_response.rs @@ -58,6 +58,9 @@ pub trait VersionChangeNotifier { /// Stop notifying `location` should the XCM change. Returns an error if there is no existing /// notification set up. fn stop(location: &MultiLocation) -> XcmResult; + + /// Return true if a location is subscribed to XCM version changes. + fn is_subscribed(location: &MultiLocation) -> bool; } impl VersionChangeNotifier for () { @@ -67,4 +70,7 @@ impl VersionChangeNotifier for () { fn stop(_: &MultiLocation) -> XcmResult { Err(XcmError::Unimplemented) } + fn is_subscribed(_: &MultiLocation) -> bool { + false + } }