1#![cfg_attr(not(feature = "std"), no_std)]
18
19extern crate alloc;
20extern crate core;
21
22use alloc::{vec, vec::Vec};
23use codec::{Decode, Encode};
24use core::{fmt::Debug, marker::PhantomData};
25use frame_support::{
26 dispatch::GetDispatchInfo,
27 ensure,
28 traits::{Contains, ContainsPair, Defensive, Get, PalletsInfoAccess},
29};
30use sp_core::defer;
31use sp_io::hashing::blake2_128;
32use sp_weights::Weight;
33use xcm::latest::{prelude::*, AssetTransferFilter};
34
35pub mod traits;
36use traits::{
37 validate_export, AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin,
38 DropAssets, Enact, EventEmitter, ExportXcm, FeeManager, FeeReason, HandleHrmpChannelAccepted,
39 HandleHrmpChannelClosing, HandleHrmpNewChannelOpenRequest, OnResponse, ProcessTransaction,
40 Properties, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader,
41 XcmAssetTransfers,
42};
43
44pub use traits::RecordXcm;
45
46mod assets;
47pub use assets::AssetsInHolding;
48mod config;
49use crate::assets::BackupAssetsInHolding;
50pub use config::Config;
51
52pub mod test_helpers;
53#[cfg(test)]
54mod tests;
55
56#[derive(Copy, Clone, Debug, PartialEq, Eq)]
58pub struct FeesMode {
59 pub jit_withdraw: bool,
64}
65
66pub const RECURSION_LIMIT: u8 = 10;
73
74environmental::environmental!(recursion_count: u8);
75
76pub struct XcmExecutor<Config: config::Config> {
78 holding: AssetsInHolding,
79 holding_limit: usize,
80 context: XcmContext,
81 original_origin: Location,
82 trader: Config::Trader,
83 error: Option<(u32, XcmError)>,
86 total_surplus: Weight,
91 total_refunded: Weight,
92 error_handler: Xcm<Config::RuntimeCall>,
93 error_handler_weight: Weight,
94 appendix: Xcm<Config::RuntimeCall>,
95 appendix_weight: Weight,
96 transact_status: MaybeErrorCode,
97 fees_mode: FeesMode,
98 fees: AssetsInHolding,
99 asset_used_in_buy_execution: Option<AssetId>,
102 message_weight: Weight,
104 asset_claimer: Option<Location>,
105 already_paid_fees: bool,
106 _config: PhantomData<Config>,
107}
108
109#[cfg(any(test, feature = "runtime-benchmarks"))]
110impl<Config: config::Config> XcmExecutor<Config> {
111 pub fn holding(&self) -> &AssetsInHolding {
112 &self.holding
113 }
114 pub fn set_holding(&mut self, v: AssetsInHolding) {
115 self.holding = v
116 }
117 pub fn holding_limit(&self) -> &usize {
118 &self.holding_limit
119 }
120 pub fn set_holding_limit(&mut self, v: usize) {
121 self.holding_limit = v
122 }
123 pub fn origin(&self) -> &Option<Location> {
124 &self.context.origin
125 }
126 pub fn set_origin(&mut self, v: Option<Location>) {
127 self.context.origin = v
128 }
129 pub fn original_origin(&self) -> &Location {
130 &self.original_origin
131 }
132 pub fn set_original_origin(&mut self, v: Location) {
133 self.original_origin = v
134 }
135 pub fn trader(&self) -> &Config::Trader {
136 &self.trader
137 }
138 pub fn set_trader(&mut self, v: Config::Trader) {
139 self.trader = v
140 }
141 pub fn error(&self) -> &Option<(u32, XcmError)> {
142 &self.error
143 }
144 pub fn set_error(&mut self, v: Option<(u32, XcmError)>) {
145 self.error = v
146 }
147 pub fn total_surplus(&self) -> &Weight {
148 &self.total_surplus
149 }
150 pub fn set_total_surplus(&mut self, v: Weight) {
151 self.total_surplus = v
152 }
153 pub fn total_refunded(&self) -> &Weight {
154 &self.total_refunded
155 }
156 pub fn set_total_refunded(&mut self, v: Weight) {
157 self.total_refunded = v
158 }
159 pub fn error_handler(&self) -> &Xcm<Config::RuntimeCall> {
160 &self.error_handler
161 }
162 pub fn set_error_handler(&mut self, v: Xcm<Config::RuntimeCall>) {
163 self.error_handler = v
164 }
165 pub fn error_handler_weight(&self) -> &Weight {
166 &self.error_handler_weight
167 }
168 pub fn set_error_handler_weight(&mut self, v: Weight) {
169 self.error_handler_weight = v
170 }
171 pub fn appendix(&self) -> &Xcm<Config::RuntimeCall> {
172 &self.appendix
173 }
174 pub fn set_appendix(&mut self, v: Xcm<Config::RuntimeCall>) {
175 self.appendix = v
176 }
177 pub fn appendix_weight(&self) -> &Weight {
178 &self.appendix_weight
179 }
180 pub fn set_appendix_weight(&mut self, v: Weight) {
181 self.appendix_weight = v
182 }
183 pub fn transact_status(&self) -> &MaybeErrorCode {
184 &self.transact_status
185 }
186 pub fn set_transact_status(&mut self, v: MaybeErrorCode) {
187 self.transact_status = v
188 }
189 pub fn fees_mode(&self) -> &FeesMode {
190 &self.fees_mode
191 }
192 pub fn set_fees_mode(&mut self, v: FeesMode) {
193 self.fees_mode = v
194 }
195 pub fn fees(&self) -> &AssetsInHolding {
196 &self.fees
197 }
198 pub fn set_fees(&mut self, value: AssetsInHolding) {
199 self.fees = value;
200 }
201 pub fn topic(&self) -> &Option<[u8; 32]> {
202 &self.context.topic
203 }
204 pub fn set_topic(&mut self, v: Option<[u8; 32]>) {
205 self.context.topic = v;
206 }
207 pub fn asset_claimer(&self) -> Option<Location> {
208 self.asset_claimer.clone()
209 }
210 pub fn set_message_weight(&mut self, weight: Weight) {
211 self.message_weight = weight;
212 }
213 pub fn already_paid_fees(&self) -> bool {
214 self.already_paid_fees
215 }
216}
217
218pub struct WeighedMessage<Call>(Weight, Xcm<Call>);
219impl<C> PreparedMessage for WeighedMessage<C> {
220 fn weight_of(&self) -> Weight {
221 self.0
222 }
223}
224
225#[cfg(any(test, feature = "std"))]
226impl<C> WeighedMessage<C> {
227 pub fn new(weight: Weight, message: Xcm<C>) -> Self {
228 Self(weight, message)
229 }
230}
231
232impl<Config: config::Config> ExecuteXcm<Config::RuntimeCall> for XcmExecutor<Config> {
233 type Prepared = WeighedMessage<Config::RuntimeCall>;
234 fn prepare(
235 mut message: Xcm<Config::RuntimeCall>,
236 weight_limit: Weight,
237 ) -> Result<Self::Prepared, InstructionError> {
238 match Config::Weigher::weight(&mut message, weight_limit) {
239 Ok(weight) => Ok(WeighedMessage(weight, message)),
240 Err(error) => {
241 tracing::debug!(
242 target: "xcm::prepare",
243 ?error,
244 ?message,
245 "Failed to calculate weight for XCM message; execution aborted"
246 );
247 Err(error)
248 },
249 }
250 }
251 fn execute(
252 origin: impl Into<Location>,
253 WeighedMessage(xcm_weight, mut message): WeighedMessage<Config::RuntimeCall>,
254 id: &mut XcmHash,
255 weight_credit: Weight,
256 ) -> Outcome {
257 let origin = origin.into();
258 tracing::trace!(
259 target: "xcm::execute",
260 ?origin,
261 ?message,
262 ?id,
263 ?weight_credit,
264 "Executing message",
265 );
266 let mut properties = Properties { weight_credit, message_id: None };
267
268 if Config::XcmRecorder::should_record() {
271 Config::XcmRecorder::record(message.clone().into());
272 }
273
274 if let Err(e) = Config::Barrier::should_execute(
275 &origin,
276 message.inner_mut(),
277 xcm_weight,
278 &mut properties,
279 ) {
280 tracing::trace!(
281 target: "xcm::execute",
282 ?origin,
283 ?message,
284 ?properties,
285 error = ?e,
286 "Barrier blocked execution",
287 );
288
289 return Outcome::Incomplete {
290 used: xcm_weight, error: InstructionError { index: 0, error: XcmError::Barrier }, };
293 }
294
295 *id = properties.message_id.unwrap_or(*id);
296
297 let mut vm = Self::new(origin, *id);
298 vm.message_weight = xcm_weight;
299
300 while !message.0.is_empty() {
301 let result = vm.process(message);
302 tracing::trace!(target: "xcm::execute", ?result, "Message executed");
303 message = match result {
304 Err(error) => {
305 vm.total_surplus.saturating_accrue(error.weight);
306 vm.error = Some((error.index, error.xcm_error));
307 vm.take_error_handler().or_else(|| vm.take_appendix())
308 },
309 Ok(()) => {
310 vm.drop_error_handler();
311 vm.take_appendix()
312 },
313 }
314 }
315
316 vm.post_process(xcm_weight)
317 }
318
319 fn charge_fees(origin: impl Into<Location>, fees: Assets) -> XcmResult {
320 let origin = origin.into();
321 if !Config::FeeManager::is_waived(Some(&origin), FeeReason::ChargeFees) {
322 let mut charged = AssetsInHolding::new();
323 for asset in fees.inner() {
324 let withdrawn = Config::AssetTransactor::withdraw_asset(&asset, &origin, None)?;
325 charged.subsume_assets(withdrawn);
326 }
327 Config::FeeManager::handle_fee(charged, None, FeeReason::ChargeFees);
328 }
329 Ok(())
330 }
331}
332
333impl<Config: config::Config> XcmAssetTransfers for XcmExecutor<Config> {
334 type IsReserve = Config::IsReserve;
335 type IsTeleporter = Config::IsTeleporter;
336 type AssetTransactor = Config::AssetTransactor;
337}
338
339impl<Config: config::Config> FeeManager for XcmExecutor<Config> {
340 fn is_waived(origin: Option<&Location>, r: FeeReason) -> bool {
341 Config::FeeManager::is_waived(origin, r)
342 }
343
344 fn handle_fee(fee: AssetsInHolding, context: Option<&XcmContext>, r: FeeReason) {
345 Config::FeeManager::handle_fee(fee, context, r)
346 }
347}
348
349#[derive(Debug, PartialEq)]
350pub struct ExecutorError {
351 pub index: u32,
352 pub xcm_error: XcmError,
353 pub weight: Weight,
354}
355
356#[cfg(feature = "runtime-benchmarks")]
357impl From<ExecutorError> for frame_benchmarking::BenchmarkError {
358 fn from(error: ExecutorError) -> Self {
359 tracing::error!(
360 index = ?error.index,
361 xcm_error = ?error.xcm_error,
362 weight = ?error.weight,
363 "XCM ERROR",
364 );
365 Self::Stop("xcm executor error: see error logs")
366 }
367}
368
369impl<Config: config::Config> XcmExecutor<Config> {
370 pub fn new(origin: impl Into<Location>, message_id: XcmHash) -> Self {
371 let origin = origin.into();
372 Self {
373 holding: AssetsInHolding::new(),
374 holding_limit: Config::MaxAssetsIntoHolding::get() as usize,
375 context: XcmContext { origin: Some(origin.clone()), message_id, topic: None },
376 original_origin: origin,
377 trader: Config::Trader::new(),
378 error: None,
379 total_surplus: Weight::zero(),
380 total_refunded: Weight::zero(),
381 error_handler: Xcm(vec![]),
382 error_handler_weight: Weight::zero(),
383 appendix: Xcm(vec![]),
384 appendix_weight: Weight::zero(),
385 transact_status: Default::default(),
386 fees_mode: FeesMode { jit_withdraw: false },
387 fees: AssetsInHolding::new(),
388 asset_used_in_buy_execution: None,
389 message_weight: Weight::zero(),
390 asset_claimer: None,
391 already_paid_fees: false,
392 _config: PhantomData,
393 }
394 }
395
396 pub fn post_process(mut self, xcm_weight: Weight) -> Outcome {
400 let _ = self.refund_surplus();
403 drop(self.trader);
404
405 let mut weight_used = xcm_weight.saturating_sub(self.total_surplus);
406
407 if !self.holding.is_empty() {
408 tracing::trace!(
409 target: "xcm::post_process",
410 holding_register = ?self.holding,
411 context = ?self.context,
412 original_origin = ?self.original_origin,
413 "Trapping assets in holding register",
414 );
415 let claimer = self
416 .asset_claimer
417 .as_ref()
418 .or(self.context.origin.as_ref())
419 .unwrap_or(&self.original_origin);
420 let trap_weight = Config::AssetTrap::drop_assets(claimer, self.holding, &self.context);
421 weight_used.saturating_accrue(trap_weight);
422 };
423
424 match self.error {
425 None => Outcome::Complete { used: weight_used },
426 Some((index, error)) => {
429 tracing::trace!(
430 target: "xcm::post_process",
431 instruction = ?index,
432 ?error,
433 original_origin = ?self.original_origin,
434 "Execution failed",
435 );
436 Outcome::Incomplete {
437 used: weight_used,
438 error: InstructionError { index: index.try_into().unwrap_or(u8::MAX), error },
439 }
440 },
441 }
442 }
443
444 fn origin_ref(&self) -> Option<&Location> {
445 self.context.origin.as_ref()
446 }
447
448 fn cloned_origin(&self) -> Option<Location> {
449 self.context.origin.clone()
450 }
451
452 fn send(
455 &mut self,
456 dest: Location,
457 msg: Xcm<()>,
458 reason: FeeReason,
459 ) -> Result<XcmHash, XcmError> {
460 let mut msg = msg;
461 if !matches!(msg.last(), Some(SetTopic(_))) {
465 let topic_id = self.context.topic_or_message_id();
466 msg.0.push(SetTopic(topic_id.into()));
467 }
468 tracing::trace!(
469 target: "xcm::send",
470 ?msg,
471 destination = ?dest,
472 reason = ?reason,
473 "Sending msg",
474 );
475 let (ticket, fee) = validate_send::<Config::XcmSender>(dest.clone(), msg)?;
476 self.take_fee(fee, reason)?;
477 match Config::XcmSender::deliver(ticket) {
478 Ok(message_id) => {
479 Config::XcmEventEmitter::emit_sent_event(
480 self.original_origin.clone(),
481 dest,
482 None, message_id,
485 );
486 Ok(message_id)
487 },
488 Err(error) => {
489 tracing::debug!(target: "xcm::send", ?error, "XCM failed to deliver with error");
490 Config::XcmEventEmitter::emit_send_failure_event(
491 self.original_origin.clone(),
492 dest,
493 error.clone(),
494 self.context.topic_or_message_id(),
495 );
496 Err(error.into())
497 },
498 }
499 }
500
501 fn take_error_handler(&mut self) -> Xcm<Config::RuntimeCall> {
503 let mut r = Xcm::<Config::RuntimeCall>(vec![]);
504 core::mem::swap(&mut self.error_handler, &mut r);
505 self.error_handler_weight = Weight::zero();
506 r
507 }
508
509 fn drop_error_handler(&mut self) {
511 self.error_handler = Xcm::<Config::RuntimeCall>(vec![]);
512 self.total_surplus.saturating_accrue(self.error_handler_weight);
513 self.error_handler_weight = Weight::zero();
514 }
515
516 fn take_appendix(&mut self) -> Xcm<Config::RuntimeCall> {
518 let mut r = Xcm::<Config::RuntimeCall>(vec![]);
519 core::mem::swap(&mut self.appendix, &mut r);
520 self.appendix_weight = Weight::zero();
521 r
522 }
523
524 fn ensure_can_subsume_assets(&self, assets_length: usize) -> Result<(), XcmError> {
525 let worst_case_holding_len = self.holding.len() + assets_length;
530 tracing::trace!(
531 target: "xcm::ensure_can_subsume_assets",
532 ?worst_case_holding_len,
533 holding_limit = ?self.holding_limit,
534 "Ensuring subsume assets work",
535 );
536 ensure!(worst_case_holding_len <= self.holding_limit * 2, XcmError::HoldingWouldOverflow);
537 Ok(())
538 }
539
540 fn refund_surplus(&mut self) -> Result<(), XcmError> {
542 let current_surplus = self.total_surplus.saturating_sub(self.total_refunded);
543 tracing::trace!(
544 target: "xcm::refund_surplus",
545 total_surplus = ?self.total_surplus,
546 total_refunded = ?self.total_refunded,
547 ?current_surplus,
548 "Refunding surplus",
549 );
550 if current_surplus.any_gt(Weight::zero()) {
551 if let Some(refund) = self.trader.refund_weight(current_surplus, &self.context) {
552 if refund
555 .fungible
556 .first_key_value()
557 .map(|(id, _)| {
558 !self.holding.fungible.contains_key(id) &&
559 self.ensure_can_subsume_assets(1).is_err()
560 })
561 .unwrap_or(false)
562 {
563 let _ = self
567 .trader
568 .buy_weight(current_surplus, refund, &self.context)
569 .defensive_proof(
570 "refund_weight returned an asset capable of buying weight; qed",
571 );
572 tracing::error!(
573 target: "xcm::refund_surplus",
574 "error: HoldingWouldOverflow",
575 );
576 return Err(XcmError::HoldingWouldOverflow);
577 }
578 self.total_refunded.saturating_accrue(current_surplus);
579 self.holding.subsume_assets(refund);
580 }
581 }
582 if !self.fees.is_empty() {
584 let leftover_fees = self.fees.saturating_take(Wild(All));
585 tracing::trace!(
586 target: "xcm::refund_surplus",
587 ?leftover_fees,
588 );
589 self.holding.subsume_assets(leftover_fees);
590 }
591 tracing::trace!(
592 target: "xcm::refund_surplus",
593 total_refunded = ?self.total_refunded,
594 );
595 Ok(())
596 }
597
598 fn take_fee(&mut self, fees: Assets, reason: FeeReason) -> XcmResult {
601 if Config::FeeManager::is_waived(self.origin_ref(), reason.clone()) {
602 return Ok(());
603 }
604 tracing::trace!(
605 target: "xcm::fees",
606 ?fees,
607 origin_ref = ?self.origin_ref(),
608 fees_mode = ?self.fees_mode,
609 ?reason,
610 "Taking fees",
611 );
612 let Some(asset_needed_for_fees) = fees.get(0) else {
614 return Ok(()); };
616 let asset_to_pay_for_fees =
618 self.calculate_asset_for_delivery_fees(asset_needed_for_fees.clone());
619 tracing::trace!(target: "xcm::fees", ?asset_to_pay_for_fees);
620 let withdrawn_fee_asset: AssetsInHolding = if self.fees_mode.jit_withdraw {
622 let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
623 let credit = Config::AssetTransactor::withdraw_asset(
624 &asset_to_pay_for_fees,
625 origin,
626 Some(&self.context),
627 )?;
628 tracing::trace!(target: "xcm::fees", ?asset_needed_for_fees);
629 credit
630 } else {
631 let assets_to_pay_delivery_fees: AssetsInHolding = if self.fees.is_empty() {
634 self.holding.try_take(asset_to_pay_for_fees.clone().into()).map_err(|e| {
636 tracing::error!(target: "xcm::fees", ?e, ?asset_to_pay_for_fees,
637 "Holding doesn't hold enough for fees");
638 XcmError::NotHoldingFees
639 })?
640 } else {
641 self.fees.try_take(asset_to_pay_for_fees.clone().into()).map_err(|e| {
643 tracing::error!(target: "xcm::fees", ?e, ?asset_to_pay_for_fees,
644 "Fees register doesn't hold enough for fees");
645 XcmError::NotHoldingFees
646 })?
647 };
648 tracing::trace!(target: "xcm::fees", ?assets_to_pay_delivery_fees);
649 assets_to_pay_delivery_fees
650 };
651 let paid = if asset_to_pay_for_fees.id != asset_needed_for_fees.id {
653 Config::AssetExchanger::exchange_asset(
654 self.origin_ref(),
655 withdrawn_fee_asset,
656 &asset_needed_for_fees.clone().into(),
657 false,
658 )
659 .map_err(|given_assets| {
660 tracing::error!(
661 target: "xcm::fees",
662 ?given_assets, ?asset_needed_for_fees, "Swap was deemed necessary but couldn't be done:",
663 );
664 self.fees.subsume_assets(given_assets);
665 XcmError::FeesNotMet
666 })?
667 } else {
668 withdrawn_fee_asset
672 };
673 Config::FeeManager::handle_fee(paid, Some(&self.context), reason);
674 Ok(())
675 }
676
677 fn calculate_asset_for_delivery_fees(&self, asset_needed_for_fees: Asset) -> Asset {
684 let Some(asset_wanted_for_fees) =
685 self.fees.fungible.first_key_value().map(|(id, _)| id).or_else(|| {
687 self.asset_used_in_buy_execution.as_ref()
689 })
690 .filter(|&id| asset_needed_for_fees.id.ne(id))
692 else {
693 return asset_needed_for_fees
695 };
696 Config::AssetExchanger::quote_exchange_price(
697 &(asset_wanted_for_fees.clone(), Fungible(0)).into(),
698 &asset_needed_for_fees.clone().into(),
699 false, )
701 .and_then(|necessary_assets| {
702 necessary_assets.into_inner().into_iter().next()
706 })
707 .unwrap_or_else(|| {
708 tracing::trace!(
711 target: "xcm::calculate_asset_for_delivery_fees",
712 ?asset_wanted_for_fees, "Could not convert fees",
713 );
714 asset_needed_for_fees
715 })
716 }
717
718 fn to_querier(
720 local_querier: Option<Location>,
721 destination: &Location,
722 ) -> Result<Option<Location>, XcmError> {
723 Ok(match local_querier {
724 None => None,
725 Some(q) => Some(
726 q.reanchored(&destination, &Config::UniversalLocation::get()).map_err(|e| {
727 tracing::error!(target: "xcm::xcm_executor::to_querier", ?e, ?destination, "Failed to re-anchor local_querier");
728 XcmError::ReanchorFailed
729 })?,
730 ),
731 })
732 }
733
734 fn respond(
738 &mut self,
739 local_querier: Option<Location>,
740 response: Response,
741 info: QueryResponseInfo,
742 fee_reason: FeeReason,
743 ) -> Result<XcmHash, XcmError> {
744 let querier = Self::to_querier(local_querier, &info.destination)?;
745 let QueryResponseInfo { destination, query_id, max_weight } = info;
746 let instruction = QueryResponse { query_id, response, max_weight, querier };
747 let message = Xcm(vec![instruction]);
748 self.send(destination, message, fee_reason)
749 }
750
751 fn do_reserve_deposit_assets(
752 assets: AssetsInHolding,
753 dest: &Location,
754 remote_xcm: &mut Vec<Instruction<()>>,
755 context: Option<&XcmContext>,
756 ) -> Result<Assets, XcmError> {
757 let reanchored_assets = Self::reanchored_assets(&assets, dest);
758 Self::deposit_assets_with_retry(assets, dest, context)?;
759 remote_xcm.push(ReserveAssetDeposited(reanchored_assets.clone()));
760
761 Ok(reanchored_assets)
762 }
763
764 fn do_reserve_withdraw_assets(
765 assets: AssetsInHolding,
766 failed_bin: &mut AssetsInHolding,
767 reserve: &Location,
768 remote_xcm: &mut Vec<Instruction<()>>,
769 ) -> Result<Assets, XcmError> {
770 #[cfg(not(any(test, feature = "runtime-benchmarks")))]
772 for asset in assets.assets_iter() {
773 ensure!(
774 Config::IsReserve::contains(&asset, &reserve),
775 XcmError::UntrustedReserveLocation
776 );
777 }
778 let reanchored_assets =
781 assets.reanchor_and_burn_local(reserve, &Config::UniversalLocation::get(), failed_bin);
782 remote_xcm.push(WithdrawAsset(reanchored_assets.clone()));
783
784 Ok(reanchored_assets)
785 }
786
787 fn do_teleport_assets(
788 assets: AssetsInHolding,
789 dest: &Location,
790 remote_xcm: &mut Vec<Instruction<()>>,
791 context: &XcmContext,
792 ) -> Result<Assets, XcmError> {
793 let reanchored_assets = Self::reanchored_assets(&assets, dest);
794 for asset in assets.assets_iter() {
795 #[cfg(not(any(test, feature = "runtime-benchmarks")))]
797 ensure!(
798 Config::IsTeleporter::contains(&asset, &dest),
799 XcmError::UntrustedTeleportLocation
800 );
801 Config::AssetTransactor::can_check_out(dest, &asset, context)?;
806 }
807 for asset in assets.assets_iter() {
808 Config::AssetTransactor::check_out(dest, &asset, context);
809 }
810 remote_xcm.push(ReceiveTeleportedAsset(reanchored_assets.clone()));
811
812 Ok(reanchored_assets)
813 }
814
815 fn try_reanchor<T: Reanchorable>(
816 reanchorable: T,
817 destination: &Location,
818 ) -> Result<(T, InteriorLocation), XcmError> {
819 let reanchor_context = Config::UniversalLocation::get();
820 let reanchored =
821 reanchorable.reanchored(&destination, &reanchor_context).map_err(|error| {
822 tracing::error!(target: "xcm::reanchor", ?error, ?destination, ?reanchor_context, "Failed reanchoring with error.");
823 XcmError::ReanchorFailed
824 })?;
825 Ok((reanchored, reanchor_context))
826 }
827
828 fn reanchored_assets(assets: &AssetsInHolding, dest: &Location) -> Assets {
830 assets.reanchored_assets(dest, &Config::UniversalLocation::get())
831 }
832
833 #[cfg(any(test, feature = "runtime-benchmarks"))]
834 pub fn bench_process(&mut self, xcm: Xcm<Config::RuntimeCall>) -> Result<(), ExecutorError> {
835 self.process(xcm)
836 }
837
838 #[cfg(any(test, feature = "runtime-benchmarks"))]
839 pub fn bench_post_process(self, xcm_weight: Weight) -> Outcome {
840 self.post_process(xcm_weight)
841 }
842
843 fn process(&mut self, xcm: Xcm<Config::RuntimeCall>) -> Result<(), ExecutorError> {
844 tracing::trace!(
845 target: "xcm::process",
846 origin = ?self.origin_ref(),
847 total_surplus = ?self.total_surplus,
848 total_refunded = ?self.total_refunded,
849 error_handler_weight = ?self.error_handler_weight,
850 );
851 let mut result = Ok(());
852 for (i, mut instr) in xcm.0.into_iter().enumerate() {
853 match &mut result {
854 r @ Ok(()) => {
855 let inst_res = recursion_count::using_once(&mut 1, || {
858 recursion_count::with(|count| {
859 if *count > RECURSION_LIMIT {
860 return None;
861 }
862 *count = count.saturating_add(1);
863 Some(())
864 })
865 .flatten()
866 .ok_or(XcmError::ExceedsStackLimit)?;
867
868 defer! {
871 recursion_count::with(|count| {
872 *count = count.saturating_sub(1);
873 });
874 }
875
876 self.process_instruction(instr)
877 });
878 if let Err(error) = inst_res {
879 tracing::debug!(
880 target: "xcm::process",
881 ?error, "XCM execution failed at instruction index={i}"
882 );
883 Config::XcmEventEmitter::emit_process_failure_event(
884 self.original_origin.clone(),
885 error,
886 self.context.topic_or_message_id(),
887 );
888 *r = Err(ExecutorError {
889 index: i as u32,
890 xcm_error: error,
891 weight: Weight::zero(),
892 });
893 }
894 },
895 Err(ref mut error) => {
896 if let Ok(x) = Config::Weigher::instr_weight(&mut instr) {
897 error.weight.saturating_accrue(x)
898 }
899 },
900 }
901 }
902 result
903 }
904
905 fn transactional_process(
908 &mut self,
909 f: impl FnOnce(&mut Self) -> Result<(), XcmError>,
910 ) -> Result<(), XcmError> {
911 self.transactional_process_with_custom_rollback(f, |_| {})
912 }
913
914 fn transactional_process_with_custom_rollback(
920 &mut self,
921 f: impl FnOnce(&mut Self) -> Result<(), XcmError>,
922 on_rollback: impl FnOnce(&mut Self),
923 ) -> Result<(), XcmError> {
924 let mut backup_holding = BackupAssetsInHolding::safe_backup(&self.holding);
925 let mut backup_fees = BackupAssetsInHolding::safe_backup(&self.fees);
926 let result = Config::TransactionalProcessor::process(|| f(self));
927 if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() {
928 backup_holding.restore_into(&mut self.holding);
929 backup_fees.restore_into(&mut self.fees);
930 on_rollback(self);
931 }
932 result
933 }
934
935 fn process_instruction(
937 &mut self,
938 instr: Instruction<Config::RuntimeCall>,
939 ) -> Result<(), XcmError> {
940 tracing::trace!(
941 target: "xcm::process_instruction",
942 instruction = ?instr,
943 "Processing instruction",
944 );
945
946 match instr {
947 WithdrawAsset(assets) => {
948 self.ensure_can_subsume_assets(assets.len())?;
949 Config::TransactionalProcessor::process(|| {
950 let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
951 let mut total_surplus = Weight::zero();
952 let mut withdrawn = AssetsInHolding::new();
953 for asset in assets.inner() {
955 let (credit, surplus) = Config::AssetTransactor::withdraw_asset_with_surplus(
956 asset,
957 origin,
958 Some(&self.context),
959 )?;
960 withdrawn.subsume_assets(credit);
961 total_surplus.saturating_accrue(surplus);
963 }
964 self.holding.subsume_assets(withdrawn);
966 self.total_surplus.saturating_accrue(total_surplus);
968 Ok(())
969 })
970 },
971 ReserveAssetDeposited(assets) => {
972 self.ensure_can_subsume_assets(assets.len())?;
973 Config::TransactionalProcessor::process(|| {
974 let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
976 let mut minted_assets = AssetsInHolding::new();
980 for asset in assets.inner() {
981 ensure!(
983 Config::IsReserve::contains(asset, origin),
984 XcmError::UntrustedReserveLocation
985 );
986 Config::AssetTransactor::mint_asset(asset, &self.context)
987 .map(|minted| minted_assets.subsume_assets(minted))?;
988 }
989 self.holding.subsume_assets(minted_assets);
990 Ok(())
991 })
992 },
993 TransferAsset { assets, beneficiary } => {
994 Config::TransactionalProcessor::process(|| {
995 let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
997 let mut total_surplus = Weight::zero();
998 for asset in assets.inner() {
999 let (_, surplus) = Config::AssetTransactor::transfer_asset_with_surplus(
1000 &asset,
1001 origin,
1002 &beneficiary,
1003 &self.context,
1004 )?;
1005 total_surplus.saturating_accrue(surplus);
1007 }
1008 self.total_surplus.saturating_accrue(total_surplus);
1010 Ok(())
1011 })
1012 },
1013 TransferReserveAsset { mut assets, dest, xcm } => {
1014 Config::TransactionalProcessor::process(|| {
1015 let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
1016 let mut total_surplus = Weight::zero();
1017 for asset in assets.inner() {
1019 let (_, surplus) = Config::AssetTransactor::transfer_asset_with_surplus(
1020 asset,
1021 origin,
1022 &dest,
1023 &self.context,
1024 )?;
1025 total_surplus.saturating_accrue(surplus);
1027 }
1028 let reanchor_context = Config::UniversalLocation::get();
1029 assets
1030 .reanchor(&dest, &reanchor_context)
1031 .map_err(|()| {
1032 tracing::debug!(
1033 target: "xcm::process_instruction::transfer_reserve_asset",
1034 ?assets,
1035 ?dest,
1036 ?reanchor_context,
1037 "Failed to reanchor assets to destination in context"
1038 );
1039 XcmError::LocationFull
1040 })?;
1041 let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin];
1042 message.extend(xcm.0.into_iter());
1043 self.send(dest, Xcm(message), FeeReason::TransferReserveAsset)?;
1044 self.total_surplus.saturating_accrue(total_surplus);
1046 Ok(())
1047 })
1048 },
1049 ReceiveTeleportedAsset(assets) => {
1050 self.ensure_can_subsume_assets(assets.len())?;
1051 Config::TransactionalProcessor::process(|| {
1052 let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
1053 let mut minted_assets = AssetsInHolding::new();
1054 for asset in assets.inner() {
1056 ensure!(
1059 Config::IsTeleporter::contains(asset, origin),
1060 XcmError::UntrustedTeleportLocation
1061 );
1062 Config::AssetTransactor::can_check_in(origin, asset, &self.context)?;
1067 Config::AssetTransactor::check_in(origin, asset, &self.context);
1068 Config::AssetTransactor::mint_asset(asset, &self.context)
1069 .map(|minted| minted_assets.subsume_assets(minted))?;
1070 }
1071 self.holding.subsume_assets(minted_assets);
1072 Ok(())
1073 })
1074 },
1075 Transact { origin_kind, mut call, .. } => {
1077 let origin = self.cloned_origin().ok_or_else(|| {
1078 tracing::trace!(
1079 target: "xcm::process_instruction::transact",
1080 "No origin provided",
1081 );
1082
1083 XcmError::BadOrigin
1084 })?;
1085
1086 let message_call = call.take_decoded().map_err(|_| {
1087 tracing::trace!(
1088 target: "xcm::process_instruction::transact",
1089 "Failed to decode call",
1090 );
1091
1092 XcmError::FailedToDecode
1093 })?;
1094
1095 tracing::trace!(
1096 target: "xcm::process_instruction::transact",
1097 ?call,
1098 "Processing call",
1099 );
1100
1101 if !Config::SafeCallFilter::contains(&message_call) {
1102 tracing::trace!(
1103 target: "xcm::process_instruction::transact",
1104 "Call filtered by `SafeCallFilter`",
1105 );
1106
1107 return Err(XcmError::NoPermission)
1108 }
1109
1110 let dispatch_origin =
1111 Config::OriginConverter::convert_origin(origin.clone(), origin_kind).map_err(
1112 |_| {
1113 tracing::trace!(
1114 target: "xcm::process_instruction::transact",
1115 ?origin,
1116 ?origin_kind,
1117 "Failed to convert origin to a local origin."
1118 );
1119
1120 XcmError::BadOrigin
1121 },
1122 )?;
1123
1124 tracing::trace!(
1125 target: "xcm::process_instruction::transact",
1126 origin = ?dispatch_origin,
1127 call = ?message_call,
1128 "Dispatching call with origin",
1129 );
1130
1131 let weight = message_call.get_dispatch_info().call_weight;
1132 let maybe_actual_weight =
1133 match Config::CallDispatcher::dispatch(message_call, dispatch_origin) {
1134 Ok(post_info) => {
1135 tracing::trace!(
1136 target: "xcm::process_instruction::transact",
1137 ?post_info,
1138 "Dispatch successful"
1139 );
1140 self.transact_status = MaybeErrorCode::Success;
1141 post_info.actual_weight
1142 },
1143 Err(error_and_info) => {
1144 tracing::trace!(
1145 target: "xcm::process_instruction::transact",
1146 ?error_and_info,
1147 "Dispatch failed"
1148 );
1149
1150 self.transact_status = error_and_info.error.encode().into();
1151 error_and_info.post_info.actual_weight
1152 },
1153 };
1154 let actual_weight = maybe_actual_weight.unwrap_or(weight);
1155 let surplus = weight.saturating_sub(actual_weight);
1156 self.total_surplus.saturating_accrue(surplus);
1163 Ok(())
1164 },
1165 QueryResponse { query_id, response, max_weight, querier } => {
1166 let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
1167 Config::ResponseHandler::on_response(
1168 origin,
1169 query_id,
1170 querier.as_ref(),
1171 response,
1172 max_weight,
1173 &self.context,
1174 );
1175 Ok(())
1176 },
1177 DescendOrigin(who) => self.do_descend_origin(who),
1178 ClearOrigin => self.do_clear_origin(),
1179 ExecuteWithOrigin { .. } => Err(XcmError::Unimplemented),
1180 ReportError(response_info) => {
1181 self.respond(
1184 self.cloned_origin(),
1185 Response::ExecutionResult(self.error),
1186 response_info,
1187 FeeReason::Report,
1188 )?;
1189 Ok(())
1190 },
1191 DepositAsset { assets, beneficiary } => {
1192 self.transactional_process(|self_ref| {
1193 let deposited = self_ref.holding.saturating_take(assets);
1194 let surplus = Self::deposit_assets_with_retry(
1195 deposited,
1196 &beneficiary,
1197 Some(&self_ref.context),
1198 )?;
1199 self_ref.total_surplus.saturating_accrue(surplus);
1200 Ok(())
1201 })
1202 },
1203 DepositReserveAsset { assets, dest, xcm } => {
1204 self.transactional_process(|self_ref| {
1205 let mut assets = self_ref.holding.saturating_take(assets);
1206 let maybe_delivery_fee_from_assets = if self_ref.fees.is_empty() && !self_ref.fees_mode.jit_withdraw {
1209 self_ref.take_delivery_fee_from_assets(&mut assets, &dest, FeeReason::DepositReserveAsset, &xcm)?
1211 } else {
1212 None
1213 };
1214 let mut message = Vec::with_capacity(xcm.len() + 2);
1215 tracing::trace!(target: "xcm::DepositReserveAsset", ?assets, "Assets except delivery fee");
1216 Self::do_reserve_deposit_assets(
1217 assets,
1218 &dest,
1219 &mut message,
1220 Some(&self_ref.context),
1221 )?;
1222 message.push(ClearOrigin);
1224 message.extend(xcm.0.into_iter());
1226 if let Some(delivery_fee) = maybe_delivery_fee_from_assets {
1227 self_ref.holding.subsume_assets(delivery_fee);
1229 }
1230 self_ref.send(dest, Xcm(message), FeeReason::DepositReserveAsset)?;
1231 Ok(())
1232 })
1233 },
1234 InitiateReserveWithdraw { assets, reserve, xcm } => {
1235 self.transactional_process(|self_ref| {
1236 let mut assets = self_ref.holding.saturating_take(assets);
1237 let maybe_delivery_fee_from_assets = if self_ref.fees.is_empty() && !self_ref.fees_mode.jit_withdraw {
1240 self_ref.take_delivery_fee_from_assets(&mut assets, &reserve, FeeReason::InitiateReserveWithdraw, &xcm)?
1242 } else {
1243 None
1244 };
1245 let mut message = Vec::with_capacity(xcm.len() + 2);
1246 Self::do_reserve_withdraw_assets(
1247 assets,
1248 &mut self_ref.holding,
1249 &reserve,
1250 &mut message,
1251 )?;
1252 message.push(ClearOrigin);
1254 message.extend(xcm.0.into_iter());
1256 if let Some(delivery_fee) = maybe_delivery_fee_from_assets {
1257 self_ref.holding.subsume_assets(delivery_fee);
1259 }
1260 self_ref.send(reserve, Xcm(message), FeeReason::InitiateReserveWithdraw)?;
1261 Ok(())
1262 })
1263 },
1264 InitiateTeleport { assets, dest, xcm } => {
1265 self.transactional_process(|self_ref| {
1266 let mut assets = self_ref.holding.saturating_take(assets);
1267 let maybe_delivery_fee_from_assets = if self_ref.fees.is_empty() && !self_ref.fees_mode.jit_withdraw {
1270 self_ref.take_delivery_fee_from_assets(&mut assets, &dest, FeeReason::InitiateTeleport, &xcm)?
1272 } else {
1273 None
1274 };
1275 let mut message = Vec::with_capacity(xcm.len() + 2);
1276 Self::do_teleport_assets(assets, &dest, &mut message, &self_ref.context)?;
1277 message.push(ClearOrigin);
1279 message.extend(xcm.0.into_iter());
1281 if let Some(delivery_fee) = maybe_delivery_fee_from_assets {
1282 self_ref.holding.subsume_assets(delivery_fee);
1284 }
1285 self_ref.send(dest.clone(), Xcm(message), FeeReason::InitiateTeleport)?;
1286 Ok(())
1287 })
1288 },
1289 InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm } => {
1290 self.transactional_process(|self_ref| {
1291 let mut message = Vec::with_capacity(assets.len() + remote_xcm.len() + 2);
1292
1293 let remote_fees_paid = if let Some(remote_fees) = remote_fees {
1298 let reanchored_fees = match remote_fees {
1299 AssetTransferFilter::Teleport(fees_filter) => {
1300 let teleport_fees = self_ref
1301 .holding
1302 .try_take(fees_filter)
1303 .map_err(|error| {
1304 tracing::debug!(
1305 target: "xcm::process_instruction::initiate_transfer", ?error,
1306 "Failed to take specified teleport fees from holding"
1307 );
1308 XcmError::NotHoldingFees
1309 })?;
1310 Self::do_teleport_assets(
1311 teleport_fees,
1312 &destination,
1313 &mut message,
1314 &self_ref.context,
1315 )?
1316 },
1317 AssetTransferFilter::ReserveDeposit(fees_filter) => {
1318 let reserve_deposit_fees = self_ref
1319 .holding
1320 .try_take(fees_filter)
1321 .map_err(|error| {
1322 tracing::debug!(
1323 target: "xcm::process_instruction::initiate_transfer", ?error,
1324 "Failed to take specified reserve deposit fees from holding"
1325 );
1326 XcmError::NotHoldingFees
1327 })?;
1328 Self::do_reserve_deposit_assets(
1329 reserve_deposit_fees,
1330 &destination,
1331 &mut message,
1332 Some(&self_ref.context),
1333 )?
1334 },
1335 AssetTransferFilter::ReserveWithdraw(fees_filter) => {
1336 let reserve_withdraw_fees = self_ref
1337 .holding
1338 .try_take(fees_filter)
1339 .map_err(|error| {
1340 tracing::debug!(
1341 target: "xcm::process_instruction::initiate_transfer", ?error,
1342 "Failed to take specified reserve withdraw fees from holding"
1343 );
1344 XcmError::NotHoldingFees
1345 })?;
1346 Self::do_reserve_withdraw_assets(
1347 reserve_withdraw_fees,
1348 &mut self_ref.holding,
1349 &destination,
1350 &mut message,
1351 )?
1352 },
1353 };
1354 ensure!(reanchored_fees.len() == 1, XcmError::TooManyAssets);
1355 let fees =
1356 reanchored_fees.into_inner().pop().ok_or(XcmError::NotHoldingFees)?;
1357 message.push(PayFees { asset: fees });
1360 true
1361 } else {
1362 false
1363 };
1364
1365 for asset_filter in assets {
1367 match asset_filter {
1368 AssetTransferFilter::Teleport(assets) => Self::do_teleport_assets(
1369 self_ref.holding.saturating_take(assets),
1370 &destination,
1371 &mut message,
1372 &self_ref.context,
1373 )?,
1374 AssetTransferFilter::ReserveDeposit(assets) =>
1375 Self::do_reserve_deposit_assets(
1376 self_ref.holding.saturating_take(assets),
1377 &destination,
1378 &mut message,
1379 Some(&self_ref.context),
1380 )?,
1381 AssetTransferFilter::ReserveWithdraw(assets) =>
1382 Self::do_reserve_withdraw_assets(
1383 self_ref.holding.saturating_take(assets),
1384 &mut self_ref.holding,
1385 &destination,
1386 &mut message,
1387 )?,
1388 };
1389 }
1390
1391 match self_ref
1392 .origin_ref() {
1393 Some(origin) if preserve_origin => {
1394 if *origin != Location::here() {
1396 let reanchored_origin = Self::try_reanchor(origin.clone(), &destination)?.0;
1399 message.push(AliasOrigin(reanchored_origin));
1400 }
1401 }
1403 _ => {
1404 message.push(ClearOrigin);
1406 }
1407 }
1408
1409 if !remote_fees_paid {
1413 message
1418 .push(UnpaidExecution { weight_limit: Unlimited, check_origin: None });
1419 }
1420
1421 message.extend(remote_xcm.0.into_iter());
1423 self_ref.send(destination, Xcm(message), FeeReason::InitiateTransfer)?;
1425 Ok(())
1426 })
1427 },
1428 ReportHolding { response_info, assets } => {
1429 let context = Config::UniversalLocation::get();
1430 let assets = self.holding.min(&assets)
1431 .into_inner()
1432 .into_iter()
1433 .filter_map(|a| a.reanchored(&response_info.destination, &context).ok())
1434 .collect::<Vec<Asset>>()
1435 .into();
1436 self.respond(
1437 self.cloned_origin(),
1438 Response::Assets(assets),
1439 response_info,
1440 FeeReason::Report,
1441 )?;
1442 Ok(())
1443 },
1444 BuyExecution { fees, weight_limit } => {
1445 let Some(weight) = Option::<Weight>::from(weight_limit) else { return Ok(()) };
1450 self.asset_used_in_buy_execution = Some(fees.id.clone());
1453 tracing::trace!(
1454 target: "xcm::executor::BuyExecution",
1455 asset_used_in_buy_execution = ?self.asset_used_in_buy_execution
1456 );
1457 self.transactional_process(|self_ref| {
1458 let max_fee =
1460 self_ref.holding.try_take(fees.clone().into()).map_err(|e| {
1461 tracing::error!(target: "xcm::process_instruction::buy_execution", ?e, ?fees,
1462 "Failed to take fees from holding");
1463 XcmError::NotHoldingFees
1464 })?;
1465 let unspent = self_ref.trader.buy_weight(weight, max_fee, &self_ref.context).map_err(|(unspent, e)| {
1466 self_ref.holding.subsume_assets(unspent);
1467 e
1468 })?;
1469 self_ref.holding.subsume_assets(unspent);
1470 Ok(())
1471 })
1472 },
1473 PayFees { asset } => {
1474 if self.already_paid_fees {
1476 return Ok(());
1477 }
1478 self.already_paid_fees = true;
1480 tracing::trace!(
1482 target: "xcm::executor::PayFees",
1483 asset_for_fees = ?asset,
1484 message_weight = ?self.message_weight,
1485 );
1486 self.transactional_process_with_custom_rollback(
1488 |self_ref| {
1489 let max_fee =
1490 self_ref.holding.try_take(asset.into()).map_err(|error| {
1491 tracing::debug!(
1492 target: "xcm::process_instruction::pay_fees", ?error,
1493 "Failed to take fees from holding"
1494 );
1495 XcmError::NotHoldingFees
1496 })?;
1497 let unspent =
1498 self_ref.trader.buy_weight(self_ref.message_weight, max_fee.into(), &self_ref.context).map_err(|(unspent, e)| {
1499 self_ref.fees.subsume_assets(unspent);
1500 e
1501 })?;
1502 self_ref.fees.subsume_assets(unspent);
1504 Ok(())
1505 },
1506 |self_ref| {
1507 self_ref.already_paid_fees = false;
1508 },
1509 )
1510 },
1511 RefundSurplus => self.refund_surplus(),
1512 SetErrorHandler(mut handler) => {
1513 let handler_weight = Config::Weigher::weight(&mut handler, Weight::MAX)
1514 .map_err(|error| {
1515 tracing::debug!(
1516 target: "xcm::executor::SetErrorHandler",
1517 ?error,
1518 ?handler,
1519 "Failed to calculate weight"
1520 );
1521 XcmError::WeightNotComputable
1522 })?;
1523 self.total_surplus.saturating_accrue(self.error_handler_weight);
1524 self.error_handler = handler;
1525 self.error_handler_weight = handler_weight;
1526 Ok(())
1527 },
1528 SetAppendix(mut appendix) => {
1529 let appendix_weight = Config::Weigher::weight(&mut appendix, Weight::MAX)
1530 .map_err(|error| {
1531 tracing::debug!(
1532 target: "xcm::executor::SetErrorHandler",
1533 ?error,
1534 ?appendix,
1535 "Failed to calculate weight"
1536 );
1537 XcmError::WeightNotComputable
1538 })?;
1539 self.total_surplus.saturating_accrue(self.appendix_weight);
1540 self.appendix = appendix;
1541 self.appendix_weight = appendix_weight;
1542 Ok(())
1543 },
1544 ClearError => {
1545 self.error = None;
1546 Ok(())
1547 },
1548 SetHints { hints } => {
1549 for hint in hints.into_iter() {
1550 match hint {
1551 AssetClaimer { location } => {
1552 self.asset_claimer = Some(location)
1553 },
1554 }
1555 }
1556 Ok(())
1557 },
1558 ClaimAsset { assets, ticket } => {
1559 let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
1560 self.ensure_can_subsume_assets(assets.len())?;
1561 let claimed = Config::AssetTrap::claim_assets(origin, &ticket, &assets, &self.context);
1562 self.holding.subsume_assets(claimed.ok_or(XcmError::UnknownClaim)?);
1563 Ok(())
1564 },
1565 Trap(code) => Err(XcmError::Trap(code)),
1566 SubscribeVersion { query_id, max_response_weight } => {
1567 let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
1568 ensure!(&self.original_origin == origin, XcmError::BadOrigin);
1571 Config::SubscriptionService::start(
1572 origin,
1573 query_id,
1574 max_response_weight,
1575 &self.context,
1576 )
1577 },
1578 UnsubscribeVersion => {
1579 let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
1580 ensure!(&self.original_origin == origin, XcmError::BadOrigin);
1581 Config::SubscriptionService::stop(origin, &self.context)
1582 },
1583 BurnAsset(assets) => {
1584 self.holding.saturating_take(assets.into());
1585 Ok(())
1586 },
1587 ExpectAsset(assets) =>
1588 self.holding.ensure_contains(&assets).map_err(|e| {
1589 tracing::error!(target: "xcm::process_instruction::expect_asset", ?e, ?assets, "assets not contained in holding");
1590 XcmError::ExpectationFalse
1591 }),
1592 ExpectOrigin(origin) => {
1593 ensure!(self.context.origin == origin, XcmError::ExpectationFalse);
1594 Ok(())
1595 },
1596 ExpectError(error) => {
1597 ensure!(self.error == error, XcmError::ExpectationFalse);
1598 Ok(())
1599 },
1600 ExpectTransactStatus(transact_status) => {
1601 ensure!(self.transact_status == transact_status, XcmError::ExpectationFalse);
1602 Ok(())
1603 },
1604 QueryPallet { module_name, response_info } => {
1605 let pallets = Config::PalletInstancesInfo::infos()
1606 .into_iter()
1607 .filter(|x| x.module_name.as_bytes() == &module_name[..])
1608 .map(|x| {
1609 PalletInfo::new(
1610 x.index as u32,
1611 x.name.as_bytes().into(),
1612 x.module_name.as_bytes().into(),
1613 x.crate_version.major as u32,
1614 x.crate_version.minor as u32,
1615 x.crate_version.patch as u32,
1616 )
1617 })
1618 .collect::<Result<Vec<_>, XcmError>>()?;
1619 let QueryResponseInfo { destination, query_id, max_weight } = response_info;
1620 let response =
1621 Response::PalletsInfo(pallets.try_into().map_err(|error| {
1622 tracing::debug!(
1623 target: "xcm::process_instruction::query_pallet", ?error,
1624 "Failed to convert pallets to response info"
1625 );
1626 XcmError::Overflow
1627 })?);
1628 let querier = Self::to_querier(self.cloned_origin(), &destination)?;
1629 let instruction = QueryResponse { query_id, response, max_weight, querier };
1630 let message = Xcm(vec![instruction]);
1631 self.send(destination, message, FeeReason::QueryPallet)?;
1632 Ok(())
1633 },
1634 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => {
1635 let pallet = Config::PalletInstancesInfo::infos()
1636 .into_iter()
1637 .find(|x| x.index == index as usize)
1638 .ok_or(XcmError::PalletNotFound)?;
1639 ensure!(pallet.name.as_bytes() == &name[..], XcmError::NameMismatch);
1640 ensure!(pallet.module_name.as_bytes() == &module_name[..], XcmError::NameMismatch);
1641 let major = pallet.crate_version.major as u32;
1642 ensure!(major == crate_major, XcmError::VersionIncompatible);
1643 let minor = pallet.crate_version.minor as u32;
1644 ensure!(minor >= min_crate_minor, XcmError::VersionIncompatible);
1645 Ok(())
1646 },
1647 ReportTransactStatus(response_info) => {
1648 self.respond(
1649 self.cloned_origin(),
1650 Response::DispatchResult(self.transact_status.clone()),
1651 response_info,
1652 FeeReason::Report,
1653 )?;
1654 Ok(())
1655 },
1656 ClearTransactStatus => {
1657 self.transact_status = Default::default();
1658 Ok(())
1659 },
1660 UniversalOrigin(new_global) => {
1661 let universal_location = Config::UniversalLocation::get();
1662 ensure!(universal_location.first() != Some(&new_global), XcmError::InvalidLocation);
1663 let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?;
1664 let origin_xform = (origin, new_global);
1665 let ok = Config::UniversalAliases::contains(&origin_xform);
1666 ensure!(ok, XcmError::InvalidLocation);
1667 let (_, new_global) = origin_xform;
1668 let new_origin = Junctions::from([new_global]).relative_to(&universal_location);
1669 self.context.origin = Some(new_origin);
1670 Ok(())
1671 },
1672 ExportMessage { network, destination, xcm } => {
1673 let origin = self.context.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone();
1683 let universal_source = Config::UniversalLocation::get()
1684 .within_global(origin)
1685 .map_err(|()| {
1686 tracing::debug!(
1687 target: "xcm::process_instruction::export_message",
1688 "Failed to reanchor origin to universal location",
1689 );
1690 XcmError::Unanchored
1691 })?;
1692 let hash = (self.origin_ref(), &destination).using_encoded(blake2_128);
1693 let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0);
1694 let (ticket, fee) = validate_export::<Config::MessageExporter>(
1698 network,
1699 channel,
1700 universal_source,
1701 destination.clone(),
1702 xcm,
1703 )?;
1704 self.transactional_process(|self_ref| {
1705 self_ref.take_fee(fee, FeeReason::Export { network, destination })?;
1706 let _ = Config::MessageExporter::deliver(ticket).defensive_proof(
1707 "`deliver` called immediately after `validate_export`; \
1708 `take_fee` does not affect the validity of the ticket; qed",
1709 );
1710 Ok(())
1711 })
1712 },
1713 LockAsset { asset, unlocker } => {
1714 self.transactional_process(|self_ref| {
1715 let origin = self_ref.cloned_origin().ok_or(XcmError::BadOrigin)?;
1716 let (remote_asset, context) = Self::try_reanchor(asset.clone(), &unlocker)?;
1717 let lock_ticket =
1718 Config::AssetLocker::prepare_lock(unlocker.clone(), asset, origin.clone())?;
1719 let owner = origin.reanchored(&unlocker, &context).map_err(|e| {
1720 tracing::error!(target: "xcm::xcm_executor::process_instruction", ?e, ?unlocker, ?context, "Failed to re-anchor origin");
1721 XcmError::ReanchorFailed
1722 })?;
1723 let msg = Xcm::<()>(vec![NoteUnlockable { asset: remote_asset, owner }]);
1724 let (ticket, price) = validate_send::<Config::XcmSender>(unlocker, msg)?;
1725 self_ref.take_fee(price, FeeReason::LockAsset)?;
1726 lock_ticket.enact()?;
1727 Config::XcmSender::deliver(ticket)?;
1728 Ok(())
1729 })
1730 },
1731 UnlockAsset { asset, target } => {
1732 let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?;
1733 Config::AssetLocker::prepare_unlock(origin, asset, target)?.enact()?;
1734 Ok(())
1735 },
1736 NoteUnlockable { asset, owner } => {
1737 let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?;
1738 Config::AssetLocker::note_unlockable(origin, asset, owner)?;
1739 Ok(())
1740 },
1741 RequestUnlock { asset, locker } => {
1742 let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?;
1743 let remote_asset = Self::try_reanchor(asset.clone(), &locker)?.0;
1744 let remote_target = Self::try_reanchor(origin.clone(), &locker)?.0;
1745 let reduce_ticket = Config::AssetLocker::prepare_reduce_unlockable(
1746 locker.clone(),
1747 asset,
1748 origin.clone(),
1749 )?;
1750 let msg =
1751 Xcm::<()>(vec![UnlockAsset { asset: remote_asset, target: remote_target }]);
1752 let (ticket, price) = validate_send::<Config::XcmSender>(locker, msg)?;
1753 self.transactional_process(|self_ref| {
1754 self_ref.take_fee(price, FeeReason::RequestUnlock)?;
1755 reduce_ticket.enact()?;
1756 Config::XcmSender::deliver(ticket)?;
1757 Ok(())
1758 })
1759 },
1760 ExchangeAsset { give, want, maximal } => {
1761 self.transactional_process(|self_ref| {
1762 let give = self_ref.holding.saturating_take(give);
1763 self_ref.ensure_can_subsume_assets(want.len())?;
1764 let received = Config::AssetExchanger::exchange_asset(
1765 self_ref.origin_ref(),
1766 give,
1767 &want,
1768 maximal,
1769 ).map_err(|unspent| {
1770 self_ref.holding.subsume_assets(unspent);
1771 XcmError::NoDeal
1772 })?;
1773 self_ref.holding.subsume_assets(received);
1774 Ok(())
1775 })
1776 },
1777 SetFeesMode { jit_withdraw } => {
1778 self.fees_mode = FeesMode { jit_withdraw };
1779 Ok(())
1780 },
1781 SetTopic(topic) => {
1782 self.context.topic = Some(topic);
1783 Ok(())
1784 },
1785 ClearTopic => {
1786 self.context.topic = None;
1787 Ok(())
1788 },
1789 AliasOrigin(target) => {
1790 let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
1791 ensure!(Config::Aliasers::contains(origin, &target), XcmError::NoPermission);
1792 self.context.origin = Some(target);
1793 Ok(())
1794 },
1795 UnpaidExecution { check_origin, .. } => {
1796 ensure!(
1797 check_origin.is_none() || self.context.origin == check_origin,
1798 XcmError::BadOrigin
1799 );
1800 Ok(())
1801 },
1802 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
1803 Config::TransactionalProcessor::process(|| {
1804 Config::HrmpNewChannelOpenRequestHandler::handle(
1805 sender,
1806 max_message_size,
1807 max_capacity,
1808 )
1809 }),
1810 HrmpChannelAccepted { recipient } => Config::TransactionalProcessor::process(|| {
1811 Config::HrmpChannelAcceptedHandler::handle(recipient)
1812 }),
1813 HrmpChannelClosing { initiator, sender, recipient } =>
1814 Config::TransactionalProcessor::process(|| {
1815 Config::HrmpChannelClosingHandler::handle(initiator, sender, recipient)
1816 }),
1817 }
1818 }
1819
1820 fn do_descend_origin(&mut self, who: InteriorLocation) -> XcmResult {
1821 self.context
1822 .origin
1823 .as_mut()
1824 .ok_or(XcmError::BadOrigin)?
1825 .append_with(who)
1826 .map_err(|e| {
1827 tracing::error!(target: "xcm::do_descend_origin", ?e, "Failed to append junctions");
1828 XcmError::LocationFull
1829 })
1830 }
1831
1832 fn do_clear_origin(&mut self) -> XcmResult {
1833 self.context.origin = None;
1834 Ok(())
1835 }
1836
1837 fn deposit_assets_with_retry(
1854 to_deposit: AssetsInHolding,
1855 beneficiary: &Location,
1856 context: Option<&XcmContext>,
1857 ) -> Result<Weight, XcmError> {
1858 let mut total_surplus = Weight::zero();
1859 let mut failed_deposits = AssetsInHolding::new();
1860
1861 for single in to_deposit.into_per_asset_holdings() {
1863 match Config::AssetTransactor::deposit_asset_with_surplus(single, beneficiary, context)
1864 {
1865 Ok(surplus) => total_surplus.saturating_accrue(surplus),
1866 Err((unspent, _)) => {
1867 failed_deposits.subsume_assets(unspent);
1871 },
1872 }
1873 }
1874
1875 for single in failed_deposits.into_per_asset_holdings() {
1877 let surplus =
1878 Config::AssetTransactor::deposit_asset_with_surplus(single, beneficiary, context)
1879 .map_err(|(unspent, error)| {
1880 tracing::debug!(
1881 target: "xcm::deposit_assets_with_retry",
1882 ?error,
1883 ?unspent,
1884 "Retry-pass deposit failed"
1885 );
1886 error
1887 })?;
1888 total_surplus.saturating_accrue(surplus);
1889 }
1890
1891 Ok(total_surplus)
1892 }
1893
1894 fn take_delivery_fee_from_assets(
1899 &self,
1900 assets: &mut AssetsInHolding,
1901 destination: &Location,
1902 reason: FeeReason,
1903 xcm: &Xcm<()>,
1904 ) -> Result<Option<AssetsInHolding>, XcmError> {
1905 let to_weigh_reanchored = Self::reanchored_assets(&assets, destination);
1906 let remote_instruction = match reason {
1907 FeeReason::DepositReserveAsset => ReserveAssetDeposited(to_weigh_reanchored),
1908 FeeReason::InitiateReserveWithdraw => WithdrawAsset(to_weigh_reanchored),
1909 FeeReason::InitiateTeleport => ReceiveTeleportedAsset(to_weigh_reanchored),
1910 _ => {
1911 tracing::debug!(
1912 target: "xcm::take_delivery_fee_from_assets",
1913 "Unexpected delivery fee reason",
1914 );
1915 return Err(XcmError::NotHoldingFees);
1916 },
1917 };
1918 let mut message_to_weigh = Vec::with_capacity(xcm.len() + 2);
1919 message_to_weigh.push(remote_instruction);
1920 message_to_weigh.push(ClearOrigin);
1921 message_to_weigh.extend(xcm.0.clone().into_iter());
1922 let (_, fee) =
1923 validate_send::<Config::XcmSender>(destination.clone(), Xcm(message_to_weigh))?;
1924 let maybe_delivery_fee = fee.get(0).map(|asset_needed_for_fees| {
1925 tracing::trace!(
1926 target: "xcm::fees::take_delivery_fee_from_assets",
1927 "Asset provided to pay for fees {:?}, asset required for delivery fees: {:?}",
1928 self.asset_used_in_buy_execution, asset_needed_for_fees,
1929 );
1930 let asset_to_pay_for_fees =
1931 self.calculate_asset_for_delivery_fees(asset_needed_for_fees.clone());
1932 let delivery_fee = assets.saturating_take(asset_to_pay_for_fees.into());
1934 tracing::trace!(target: "xcm::fees::take_delivery_fee_from_assets", ?delivery_fee);
1935 delivery_fee
1936 });
1937 Ok(maybe_delivery_fee)
1938 }
1939}