lib.rs 18.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.

#![cfg_attr(not(feature = "std"), no_std)]

Gavin Wood's avatar
Gavin Wood committed
19
use frame_support::{
Shawn Tabrizi's avatar
Shawn Tabrizi committed
20
21
22
	dispatch::{Dispatchable, Weight},
	ensure,
	weights::GetDispatchInfo,
Gavin Wood's avatar
Gavin Wood committed
23
};
24
use sp_runtime::traits::Saturating;
Shawn Tabrizi's avatar
Shawn Tabrizi committed
25
use sp_std::{marker::PhantomData, prelude::*};
Gavin Wood's avatar
Gavin Wood committed
26
use xcm::latest::{
27
28
29
	Error as XcmError, ExecuteXcm,
	Instruction::{self, *},
	MultiAssets, MultiLocation, Outcome, Response, SendXcm, Xcm,
30
31
32
};

pub mod traits;
Gavin Wood's avatar
Gavin Wood committed
33
use traits::{
34
	ClaimAssets, ConvertOrigin, DropAssets, FilterAssetLocation, InvertLocation, OnResponse,
35
	ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader,
Gavin Wood's avatar
Gavin Wood committed
36
};
37

Gavin Wood's avatar
Gavin Wood committed
38
mod assets;
Gavin Wood's avatar
Gavin Wood committed
39
pub use assets::Assets;
Gavin Wood's avatar
Gavin Wood committed
40
mod config;
41
42
pub use config::Config;

Alexander Popiak's avatar
Alexander Popiak committed
43
/// The XCM executor.
44
pub struct XcmExecutor<Config: config::Config> {
45
46
47
48
	pub holding: Assets,
	pub origin: Option<MultiLocation>,
	pub original_origin: MultiLocation,
	pub trader: Config::Trader,
49
	/// The most recent error result and instruction index into the fragment in which it occurred,
50
	/// if any.
51
	pub error: Option<(u32, XcmError)>,
52
53
54
55
	/// 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`).
56
57
58
59
60
61
	pub total_surplus: u64,
	pub total_refunded: u64,
	pub error_handler: Xcm<Config::Call>,
	pub error_handler_weight: u64,
	pub appendix: Xcm<Config::Call>,
	pub appendix_weight: u64,
62
63
	_config: PhantomData<Config>,
}
64

65
66
67
/// The maximum recursion limit for `execute_xcm` and `execute_effects`.
pub const MAX_RECURSION_LIMIT: u32 = 8;

Gavin Wood's avatar
Gavin Wood committed
68
impl<Config: config::Config> ExecuteXcm<Config::Call> for XcmExecutor<Config> {
69
	fn execute_xcm_in_credit(
70
		origin: impl Into<MultiLocation>,
71
		mut message: Xcm<Config::Call>,
72
73
74
		weight_limit: Weight,
		mut weight_credit: Weight,
	) -> Outcome {
75
		let origin = origin.into();
Alexander Popiak's avatar
Alexander Popiak committed
76
77
78
79
80
81
82
83
		log::trace!(
			target: "xcm::execute_xcm_in_credit",
			"origin: {:?}, message: {:?}, weight_limit: {:?}, weight_credit: {:?}",
			origin,
			message,
			weight_limit,
			weight_credit,
		);
84
		let xcm_weight = match Config::Weigher::weight(&mut message) {
Gavin Wood's avatar
Gavin Wood committed
85
			Ok(x) => x,
86
87
88
89
90
91
92
93
94
95
96
			Err(()) => {
				log::debug!(
					target: "xcm::execute_xcm_in_credit",
					"Weight not computable! (origin: {:?}, message: {:?}, weight_limit: {:?}, weight_credit: {:?})",
					origin,
					message,
					weight_limit,
					weight_credit,
				);
				return Outcome::Error(XcmError::WeightNotComputable)
			},
Gavin Wood's avatar
Gavin Wood committed
97
		};
98
		if xcm_weight > weight_limit {
99
100
101
102
103
104
105
106
107
108
			log::debug!(
				target: "xcm::execute_xcm_in_credit",
				"Weight limit reached! weight > weight_limit: {:?} > {:?}. (origin: {:?}, message: {:?}, weight_limit: {:?}, weight_credit: {:?})",
				xcm_weight,
				weight_limit,
				origin,
				message,
				weight_limit,
				weight_credit,
			);
109
			return Outcome::Error(XcmError::WeightLimitReached(xcm_weight))
Gavin Wood's avatar
Gavin Wood committed
110
		}
111

112
		if let Err(e) =
113
114
			Config::Barrier::should_execute(&origin, &mut message, xcm_weight, &mut weight_credit)
		{
115
116
117
118
119
120
121
122
123
			log::debug!(
				target: "xcm::execute_xcm_in_credit",
				"Barrier blocked execution! Error: {:?}. (origin: {:?}, message: {:?}, weight_limit: {:?}, weight_credit: {:?})",
				e,
				origin,
				message,
				weight_limit,
				weight_credit,
			);
124
125
126
			return Outcome::Error(XcmError::Barrier)
		}

127
		let mut vm = Self::new(origin);
128
129
130
131

		while !message.0.is_empty() {
			let result = vm.execute(message);
			log::trace!(target: "xcm::execute_xcm_in_credit", "result: {:?}", result);
132
133
134
			message = if let Err(error) = result {
				vm.total_surplus.saturating_accrue(error.weight);
				vm.error = Some((error.index, error.xcm_error));
135
136
137
138
139
140
141
				vm.take_error_handler().or_else(|| vm.take_appendix())
			} else {
				vm.drop_error_handler();
				vm.take_appendix()
			}
		}

142
		vm.post_execute(xcm_weight)
Gavin Wood's avatar
Gavin Wood committed
143
144
145
	}
}

146
147
#[derive(Debug)]
pub struct ExecutorError {
148
149
150
	pub index: u32,
	pub xcm_error: XcmError,
	pub weight: u64,
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
}

#[cfg(feature = "runtime-benchmarks")]
impl From<ExecutorError> 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")
	}
}

Gavin Wood's avatar
Gavin Wood committed
166
impl<Config: config::Config> XcmExecutor<Config> {
167
168
	pub fn new(origin: impl Into<MultiLocation>) -> Self {
		let origin = origin.into();
169
170
		Self {
			holding: Assets::new(),
171
172
			origin: Some(origin.clone()),
			original_origin: origin,
173
174
175
176
177
178
179
180
181
182
			trader: Config::Trader::new(),
			error: None,
			total_surplus: 0,
			total_refunded: 0,
			error_handler: Xcm(vec![]),
			error_handler_weight: 0,
			appendix: Xcm(vec![]),
			appendix_weight: 0,
			_config: PhantomData,
		}
Gavin Wood's avatar
Gavin Wood committed
183
184
	}

185
186
	/// Execute the XCM program fragment and report back the error and which instruction caused it,
	/// or `Ok` if there was no error.
187
	pub fn execute(&mut self, xcm: Xcm<Config::Call>) -> Result<(), ExecutorError> {
Alexander Popiak's avatar
Alexander Popiak committed
188
		log::trace!(
189
190
191
192
193
194
			target: "xcm::execute",
			"origin: {:?}, total_surplus/refunded: {:?}/{:?}, error_handler_weight: {:?}",
			self.origin,
			self.total_surplus,
			self.total_refunded,
			self.error_handler_weight,
Alexander Popiak's avatar
Alexander Popiak committed
195
		);
196
197
198
199
200
		let mut result = Ok(());
		for (i, instr) in xcm.0.into_iter().enumerate() {
			match &mut result {
				r @ Ok(()) =>
					if let Err(e) = self.process_instruction(instr) {
201
						*r = Err(ExecutorError { index: i as u32, xcm_error: e, weight: 0 });
202
					},
203
				Err(ref mut error) =>
204
					if let Ok(x) = Config::Weigher::instr_weight(&instr) {
205
						error.weight.saturating_accrue(x)
206
207
					},
			}
208
		}
209
210
		result
	}
211

212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
	/// 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)
			},
		}
	}

237
238
239
240
241
242
243
	/// Remove the registered error handler and return it. Do not refund its weight.
	fn take_error_handler(&mut self) -> Xcm<Config::Call> {
		let mut r = Xcm::<Config::Call>(vec![]);
		sp_std::mem::swap(&mut self.error_handler, &mut r);
		self.error_handler_weight = 0;
		r
	}
Gavin Wood's avatar
Gavin Wood committed
244

245
246
247
	/// Drop the registered error handler and refund its weight.
	fn drop_error_handler(&mut self) {
		self.error_handler = Xcm::<Config::Call>(vec![]);
248
		self.total_surplus.saturating_accrue(self.error_handler_weight);
249
250
251
252
253
254
255
256
257
258
		self.error_handler_weight = 0;
	}

	/// Remove the registered appendix and return it.
	fn take_appendix(&mut self) -> Xcm<Config::Call> {
		let mut r = Xcm::<Config::Call>(vec![]);
		sp_std::mem::swap(&mut self.appendix, &mut r);
		self.appendix_weight = 0;
		r
	}
Gavin Wood's avatar
Gavin Wood committed
259

260
261
262
263
	/// Refund any unused weight.
	fn refund_surplus(&mut self) {
		let current_surplus = self.total_surplus.saturating_sub(self.total_refunded);
		if current_surplus > 0 {
264
			self.total_refunded.saturating_accrue(current_surplus);
265
266
267
268
269
			if let Some(w) = self.trader.refund_weight(current_surplus) {
				self.holding.subsume(w);
			}
		}
	}
Gavin Wood's avatar
Gavin Wood committed
270

271
272
273
274
	/// Process a single XCM instruction, mutating the state of the XCM virtual machine.
	fn process_instruction(&mut self, instr: Instruction<Config::Call>) -> Result<(), XcmError> {
		match instr {
			WithdrawAsset(assets) => {
275
				// Take `assets` from the origin account (on-chain) and place in holding.
276
277
278
279
				let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?;
				for asset in assets.drain().into_iter() {
					Config::AssetTransactor::withdraw_asset(&asset, origin)?;
					self.holding.subsume(asset);
280
				}
281
				Ok(())
Shawn Tabrizi's avatar
Shawn Tabrizi committed
282
			},
283
			ReserveAssetDeposited(assets) => {
284
				// check whether we trust origin to be our reserve location for this asset.
285
286
287
				let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?;
				for asset in assets.drain().into_iter() {
					// Must ensure that we recognise the asset as being managed by the origin.
Shawn Tabrizi's avatar
Shawn Tabrizi committed
288
					ensure!(
289
						Config::IsReserve::filter_asset_location(&asset, origin),
Shawn Tabrizi's avatar
Shawn Tabrizi committed
290
291
						XcmError::UntrustedReserveLocation
					);
292
					self.holding.subsume(asset);
Gavin Wood's avatar
Gavin Wood committed
293
				}
294
				Ok(())
Shawn Tabrizi's avatar
Shawn Tabrizi committed
295
			},
296
			TransferAsset { assets, beneficiary } => {
Gavin Wood's avatar
Gavin Wood committed
297
				// Take `assets` from the origin account (on-chain) and place into dest account.
298
				let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?;
Gavin Wood's avatar
Gavin Wood committed
299
				for asset in assets.inner() {
300
					Config::AssetTransactor::beam_asset(&asset, origin, &beneficiary)?;
Gavin Wood's avatar
Gavin Wood committed
301
				}
302
				Ok(())
Shawn Tabrizi's avatar
Shawn Tabrizi committed
303
			},
304
305
			TransferReserveAsset { mut assets, dest, xcm } => {
				let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?;
Gavin Wood's avatar
Gavin Wood committed
306
				// Take `assets` from the origin account (on-chain) and place into dest account.
307
308
				let inv_dest = Config::LocationInverter::invert_location(&dest)
					.map_err(|()| XcmError::MultiLocationNotInvertible)?;
Gavin Wood's avatar
Gavin Wood committed
309
				for asset in assets.inner() {
310
					Config::AssetTransactor::beam_asset(asset, origin, &dest)?;
311
				}
312
				assets.reanchor(&inv_dest).map_err(|()| XcmError::MultiLocationFull)?;
313
314
315
				let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin];
				message.extend(xcm.0.into_iter());
				Config::XcmSender::send_xcm(dest, Xcm(message)).map_err(Into::into)
Shawn Tabrizi's avatar
Shawn Tabrizi committed
316
			},
317
318
			ReceiveTeleportedAsset(assets) => {
				let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?;
319
				// check whether we trust origin to teleport this asset to us via config trait.
Gavin Wood's avatar
Gavin Wood committed
320
				for asset in assets.inner() {
321
322
					// We only trust the origin to send us assets that they identify as their
					// sovereign assets.
Shawn Tabrizi's avatar
Shawn Tabrizi committed
323
					ensure!(
324
						Config::IsTeleporter::filter_asset_location(asset, origin),
Shawn Tabrizi's avatar
Shawn Tabrizi committed
325
326
						XcmError::UntrustedTeleportLocation
					);
327
328
329
330
331
					// 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)?;
				}
332
333
334
				for asset in assets.drain().into_iter() {
					Config::AssetTransactor::check_in(origin, &asset);
					self.holding.subsume(asset);
335
				}
336
				Ok(())
Shawn Tabrizi's avatar
Shawn Tabrizi committed
337
			},
338
			Transact { origin_type, require_weight_at_most, mut call } => {
339
				// We assume that the Relay-chain is allowed to use transact on this parachain.
340
				let origin = self.origin.clone().ok_or(XcmError::BadOrigin)?;
341

Gavin Wood's avatar
Gavin Wood committed
342
343
				// TODO: #2841 #TRANSACTFILTER allow the trait to issue filters for the relay-chain
				let message_call = call.take_decoded().map_err(|_| XcmError::FailedToDecode)?;
344
345
				let dispatch_origin = Config::OriginConverter::convert_origin(origin, origin_type)
					.map_err(|_| XcmError::BadOrigin)?;
Gavin Wood's avatar
Gavin Wood committed
346
				let weight = message_call.get_dispatch_info().weight;
347
				ensure!(weight <= require_weight_at_most, XcmError::MaxWeightInvalid);
Gavin Wood's avatar
Gavin Wood committed
348
349
350
351
352
353
				let actual_weight = match message_call.dispatch(dispatch_origin) {
					Ok(post_info) => post_info.actual_weight,
					Err(error_and_info) => {
						// Not much to do with the result as it is. It's up to the parachain to ensure that the
						// message makes sense.
						error_and_info.post_info.actual_weight
Shawn Tabrizi's avatar
Shawn Tabrizi committed
354
355
356
					},
				}
				.unwrap_or(weight);
Gavin Wood's avatar
Gavin Wood committed
357
				let surplus = weight.saturating_sub(actual_weight);
358
359
360
361
362
363
364
365
				// 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).
366
				self.total_surplus.saturating_accrue(surplus);
367
				Ok(())
Shawn Tabrizi's avatar
Shawn Tabrizi committed
368
			},
369
370
371
372
			QueryResponse { query_id, response, max_weight } => {
				let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?;
				Config::ResponseHandler::on_response(origin, query_id, response, max_weight);
				Ok(())
Shawn Tabrizi's avatar
Shawn Tabrizi committed
373
			},
374
375
376
377
378
379
380
381
382
			DescendOrigin(who) => self
				.origin
				.as_mut()
				.ok_or(XcmError::BadOrigin)?
				.append_with(who)
				.map_err(|_| XcmError::MultiLocationFull),
			ClearOrigin => {
				self.origin = None;
				Ok(())
Shawn Tabrizi's avatar
Shawn Tabrizi committed
383
			},
384
385
386
			ReportError { query_id, dest, max_response_weight: max_weight } => {
				// Report the given result by sending a QueryResponse XCM to a previously given outcome
				// destination if one was registered.
387
				let response = Response::ExecutionResult(self.error);
388
389
390
391
392
393
				let message = QueryResponse { query_id, response, max_weight };
				Config::XcmSender::send_xcm(dest, Xcm(vec![message]))?;
				Ok(())
			},
			DepositAsset { assets, max_assets, beneficiary } => {
				let deposited = self.holding.limited_saturating_take(assets, max_assets as usize);
394
				for asset in deposited.into_assets_iter() {
Gavin Wood's avatar
Gavin Wood committed
395
					Config::AssetTransactor::deposit_asset(&asset, &beneficiary)?;
396
				}
397
				Ok(())
398
			},
399
400
			DepositReserveAsset { assets, max_assets, dest, xcm } => {
				let deposited = self.holding.limited_saturating_take(assets, max_assets as usize);
401
402
403
				for asset in deposited.assets_iter() {
					Config::AssetTransactor::deposit_asset(&asset, &dest)?;
				}
404
				let assets = Self::reanchored(deposited, &dest)?;
405
406
407
				let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin];
				message.extend(xcm.0.into_iter());
				Config::XcmSender::send_xcm(dest, Xcm(message)).map_err(Into::into)
408
			},
409
			InitiateReserveWithdraw { assets, reserve, xcm } => {
410
				let assets = Self::reanchored(self.holding.saturating_take(assets), &reserve)?;
411
412
413
				let mut message = vec![WithdrawAsset(assets), ClearOrigin];
				message.extend(xcm.0.into_iter());
				Config::XcmSender::send_xcm(reserve, Xcm(message)).map_err(Into::into)
Shawn Tabrizi's avatar
Shawn Tabrizi committed
414
			},
415
			InitiateTeleport { assets, dest, xcm } => {
416
				// We must do this first in order to resolve wildcards.
417
				let assets = self.holding.saturating_take(assets);
418
				for asset in assets.assets_iter() {
419
					Config::AssetTransactor::check_out(&dest, &asset);
420
				}
421
				let assets = Self::reanchored(assets, &dest)?;
422
423
424
				let mut message = vec![ReceiveTeleportedAsset(assets), ClearOrigin];
				message.extend(xcm.0.into_iter());
				Config::XcmSender::send_xcm(dest, Xcm(message)).map_err(Into::into)
Shawn Tabrizi's avatar
Shawn Tabrizi committed
425
			},
426
			QueryHolding { query_id, dest, assets, max_response_weight } => {
427
				let assets = Self::reanchored(self.holding.min(&assets), &dest)?;
428
429
430
431
				let max_weight = max_response_weight;
				let response = Response::Assets(assets);
				let instruction = QueryResponse { query_id, response, max_weight };
				Config::XcmSender::send_xcm(dest, Xcm(vec![instruction])).map_err(Into::into)
Shawn Tabrizi's avatar
Shawn Tabrizi committed
432
			},
433
434
435
436
437
438
439
440
441
442
443
			BuyExecution { fees, weight_limit } => {
				// There is no need to buy any weight is `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.
				if let Some(weight) = Option::<u64>::from(weight_limit) {
					// pay for `weight` using up to `fees` of the holding register.
					let max_fee =
						self.holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?;
					let unspent = self.trader.buy_weight(weight, max_fee)?;
					self.holding.subsume_assets(unspent);
Gavin Wood's avatar
Gavin Wood committed
444
				}
445
446
447
448
449
				Ok(())
			},
			RefundSurplus => {
				self.refund_surplus();
				Ok(())
Shawn Tabrizi's avatar
Shawn Tabrizi committed
450
			},
451
			SetErrorHandler(mut handler) => {
452
453
				let handler_weight = Config::Weigher::weight(&mut handler)
					.map_err(|()| XcmError::WeightNotComputable)?;
454
				self.total_surplus.saturating_accrue(self.error_handler_weight);
455
456
457
458
459
				self.error_handler = handler;
				self.error_handler_weight = handler_weight;
				Ok(())
			},
			SetAppendix(mut appendix) => {
460
461
				let appendix_weight = Config::Weigher::weight(&mut appendix)
					.map_err(|()| XcmError::WeightNotComputable)?;
462
				self.total_surplus.saturating_accrue(self.appendix_weight);
463
464
465
466
467
468
469
470
				self.appendix = appendix;
				self.appendix_weight = appendix_weight;
				Ok(())
			},
			ClearError => {
				self.error = None;
				Ok(())
			},
471
472
473
474
475
476
477
478
479
480
			ClaimAsset { assets, ticket } => {
				let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?;
				let ok = Config::AssetClaims::claim_assets(origin, &ticket, &assets);
				ensure!(ok, XcmError::UnknownClaim);
				for asset in assets.drain().into_iter() {
					self.holding.subsume(asset);
				}
				Ok(())
			},
			Trap(code) => Err(XcmError::Trap(code)),
481
482
483
484
485
486
487
488
489
490
491
492
			SubscribeVersion { query_id, max_response_weight } => {
				let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone();
				// 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)
			},
			UnsubscribeVersion => {
				let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?;
				ensure!(&self.original_origin == origin, XcmError::BadOrigin);
				Config::SubscriptionService::stop(origin)
			},
493
494
495
496
			ExchangeAsset { .. } => Err(XcmError::Unimplemented),
			HrmpNewChannelOpenRequest { .. } => Err(XcmError::Unimplemented),
			HrmpChannelAccepted { .. } => Err(XcmError::Unimplemented),
			HrmpChannelClosing { .. } => Err(XcmError::Unimplemented),
497
		}
498
499
	}

500
501
502
	fn reanchored(mut assets: Assets, dest: &MultiLocation) -> Result<MultiAssets, XcmError> {
		let inv_dest = Config::LocationInverter::invert_location(&dest)
			.map_err(|()| XcmError::MultiLocationNotInvertible)?;
503
		assets.prepend_location(&inv_dest);
504
		Ok(assets.into_assets_iter().collect::<Vec<_>>().into())
505
506
	}
}