1use crate::{CreateMatcher, MatchXcm};
20use core::{cell::Cell, marker::PhantomData, ops::ControlFlow, result::Result};
21use frame_support::{
22 ensure,
23 traits::{Contains, ContainsPair, Get, Nothing, ProcessMessageError},
24};
25use polkadot_parachain_primitives::primitives::IsSystem;
26use xcm::prelude::*;
27use xcm_executor::traits::{CheckSuspension, DenyExecution, OnResponse, Properties, ShouldExecute};
28
29pub struct TakeWeightCredit;
35impl ShouldExecute for TakeWeightCredit {
36 fn should_execute<RuntimeCall>(
37 origin: &Location,
38 instructions: &mut [Instruction<RuntimeCall>],
39 max_weight: Weight,
40 properties: &mut Properties,
41 ) -> Result<(), ProcessMessageError> {
42 tracing::trace!(
43 target: "xcm::barriers",
44 ?origin,
45 ?instructions,
46 ?max_weight,
47 ?properties,
48 "TakeWeightCredit"
49 );
50 properties.weight_credit = properties
51 .weight_credit
52 .checked_sub(&max_weight)
53 .ok_or(ProcessMessageError::Overweight(max_weight))?;
54 Ok(())
55 }
56}
57
58const MAX_ASSETS_FOR_BUY_EXECUTION: usize = 2;
59
60pub struct AllowTopLevelPaidExecutionFrom<T>(PhantomData<T>);
67impl<T: Contains<Location>> ShouldExecute for AllowTopLevelPaidExecutionFrom<T> {
68 fn should_execute<RuntimeCall>(
69 origin: &Location,
70 instructions: &mut [Instruction<RuntimeCall>],
71 max_weight: Weight,
72 properties: &mut Properties,
73 ) -> Result<(), ProcessMessageError> {
74 tracing::trace!(
75 target: "xcm::barriers",
76 ?origin,
77 ?instructions,
78 ?max_weight,
79 ?properties,
80 "AllowTopLevelPaidExecutionFrom",
81 );
82
83 ensure!(T::contains(origin), ProcessMessageError::Unsupported);
84 let end = instructions.len().min(5);
88 instructions[..end]
89 .matcher()
90 .match_next_inst(|inst| match inst {
91 WithdrawAsset(ref assets) |
92 ReceiveTeleportedAsset(ref assets) |
93 ReserveAssetDeposited(ref assets) |
94 ClaimAsset { ref assets, .. } => {
95 if assets.len() <= MAX_ASSETS_FOR_BUY_EXECUTION {
96 Ok(())
97 } else {
98 Err(ProcessMessageError::BadFormat)
99 }
100 },
101 _ => Err(ProcessMessageError::BadFormat),
102 })?
103 .skip_inst_while(|inst| {
104 matches!(inst, ClearOrigin | AliasOrigin(..)) ||
105 matches!(inst, DescendOrigin(child) if child != &Here) ||
106 matches!(inst, SetHints { .. })
107 })?
108 .match_next_inst(|inst| match inst {
109 BuyExecution { weight_limit: Limited(ref mut weight), .. }
110 if weight.all_gte(max_weight) =>
111 {
112 *weight = max_weight;
113 Ok(())
114 },
115 BuyExecution { ref mut weight_limit, .. } if weight_limit == &Unlimited => {
116 *weight_limit = Limited(max_weight);
117 Ok(())
118 },
119 PayFees { .. } => Ok(()),
120 _ => Err(ProcessMessageError::Overweight(max_weight)),
121 })?;
122 Ok(())
123 }
124}
125
126pub struct WithComputedOrigin<InnerBarrier, LocalUniversal, MaxPrefixes>(
172 PhantomData<(InnerBarrier, LocalUniversal, MaxPrefixes)>,
173);
174impl<InnerBarrier: ShouldExecute, LocalUniversal: Get<InteriorLocation>, MaxPrefixes: Get<u32>>
175 ShouldExecute for WithComputedOrigin<InnerBarrier, LocalUniversal, MaxPrefixes>
176{
177 fn should_execute<Call>(
178 origin: &Location,
179 instructions: &mut [Instruction<Call>],
180 max_weight: Weight,
181 properties: &mut Properties,
182 ) -> Result<(), ProcessMessageError> {
183 tracing::trace!(
184 target: "xcm::barriers",
185 ?origin,
186 ?instructions,
187 ?max_weight,
188 ?properties,
189 "WithComputedOrigin"
190 );
191 let mut actual_origin = origin.clone();
192 let skipped = Cell::new(0usize);
193 instructions.matcher().match_next_inst_while(
199 |_| skipped.get() < MaxPrefixes::get() as usize,
200 |inst| {
201 match inst {
202 UniversalOrigin(new_global) => {
203 actual_origin =
207 Junctions::from([*new_global]).relative_to(&LocalUniversal::get());
208 },
209 DescendOrigin(j) => {
210 let Ok(_) = actual_origin.append_with(j.clone()) else {
211 return Err(ProcessMessageError::Unsupported);
212 };
213 },
214 _ => return Ok(ControlFlow::Break(())),
215 };
216 skipped.set(skipped.get() + 1);
217 Ok(ControlFlow::Continue(()))
218 },
219 )?;
220 InnerBarrier::should_execute(
221 &actual_origin,
222 &mut instructions[skipped.get()..],
223 max_weight,
224 properties,
225 )
226 }
227}
228
229pub struct TrailingSetTopicAsId<InnerBarrier>(PhantomData<InnerBarrier>);
236impl<InnerBarrier: ShouldExecute> ShouldExecute for TrailingSetTopicAsId<InnerBarrier> {
237 fn should_execute<Call>(
238 origin: &Location,
239 instructions: &mut [Instruction<Call>],
240 max_weight: Weight,
241 properties: &mut Properties,
242 ) -> Result<(), ProcessMessageError> {
243 tracing::trace!(
244 target: "xcm::barriers",
245 ?origin,
246 ?instructions,
247 ?max_weight,
248 ?properties,
249 "TrailingSetTopicAsId"
250 );
251 let until = if let Some(SetTopic(t)) = instructions.last() {
252 properties.message_id = Some(*t);
253 instructions.len() - 1
254 } else {
255 instructions.len()
256 };
257 InnerBarrier::should_execute(&origin, &mut instructions[..until], max_weight, properties)
258 }
259}
260
261pub struct RespectSuspension<Inner, SuspensionChecker>(PhantomData<(Inner, SuspensionChecker)>);
264impl<Inner, SuspensionChecker> ShouldExecute for RespectSuspension<Inner, SuspensionChecker>
265where
266 Inner: ShouldExecute,
267 SuspensionChecker: CheckSuspension,
268{
269 fn should_execute<Call>(
270 origin: &Location,
271 instructions: &mut [Instruction<Call>],
272 max_weight: Weight,
273 properties: &mut Properties,
274 ) -> Result<(), ProcessMessageError> {
275 if SuspensionChecker::is_suspended(origin, instructions, max_weight, properties) {
276 Err(ProcessMessageError::Yield)
277 } else {
278 Inner::should_execute(origin, instructions, max_weight, properties)
279 }
280 }
281}
282
283pub struct AllowUnpaidExecutionFrom<T>(PhantomData<T>);
288impl<T: Contains<Location>> ShouldExecute for AllowUnpaidExecutionFrom<T> {
289 fn should_execute<RuntimeCall>(
290 origin: &Location,
291 instructions: &mut [Instruction<RuntimeCall>],
292 max_weight: Weight,
293 properties: &mut Properties,
294 ) -> Result<(), ProcessMessageError> {
295 tracing::trace!(
296 target: "xcm::barriers",
297 ?origin, ?instructions, ?max_weight, ?properties,
298 "AllowUnpaidExecutionFrom"
299 );
300 ensure!(T::contains(origin), ProcessMessageError::Unsupported);
301 Ok(())
302 }
303}
304
305pub struct AllowExplicitUnpaidExecutionFrom<T, Aliasers = Nothing>(PhantomData<(T, Aliasers)>);
329impl<T: Contains<Location>, Aliasers: ContainsPair<Location, Location>> ShouldExecute
330 for AllowExplicitUnpaidExecutionFrom<T, Aliasers>
331{
332 fn should_execute<Call>(
333 origin: &Location,
334 instructions: &mut [Instruction<Call>],
335 max_weight: Weight,
336 properties: &mut Properties,
337 ) -> Result<(), ProcessMessageError> {
338 tracing::trace!(
339 target: "xcm::barriers",
340 ?origin, ?instructions, ?max_weight, ?properties,
341 "AllowExplicitUnpaidExecutionFrom",
342 );
343 let mut actual_origin = origin.clone();
347 let processed = Cell::new(0usize);
348 let instructions_to_process = 5;
349 instructions
350 .matcher()
351 .match_next_inst_while(
353 |inst| {
354 processed.get() < instructions_to_process &&
355 matches!(
356 inst,
357 ReceiveTeleportedAsset(_) |
358 ReserveAssetDeposited(_) | WithdrawAsset(_) |
359 SetHints { .. }
360 )
361 },
362 |_| {
363 processed.set(processed.get() + 1);
364 Ok(ControlFlow::Continue(()))
365 },
366 )?
367 .match_next_inst_while(
370 |_| processed.get() < instructions_to_process,
371 |inst| {
372 match inst {
373 ClearOrigin => {
374 return Err(ProcessMessageError::Unsupported);
377 },
378 AliasOrigin(target) => {
379 if Aliasers::contains(&actual_origin, &target) {
380 actual_origin = target.clone();
381 } else {
382 return Err(ProcessMessageError::Unsupported);
383 }
384 },
385 DescendOrigin(child) if child != &Here => {
386 let Ok(_) = actual_origin.append_with(child.clone()) else {
387 return Err(ProcessMessageError::Unsupported);
388 };
389 },
390 _ => return Ok(ControlFlow::Break(())),
391 };
392 processed.set(processed.get() + 1);
393 Ok(ControlFlow::Continue(()))
394 },
395 )?
396 .match_next_inst(|inst| match inst {
398 UnpaidExecution { weight_limit: Limited(m), .. } if m.all_gte(max_weight) => Ok(()),
399 UnpaidExecution { weight_limit: Unlimited, .. } => Ok(()),
400 _ => Err(ProcessMessageError::Overweight(max_weight)),
401 })?;
402
403 ensure!(T::contains(&actual_origin), ProcessMessageError::Unsupported);
406
407 Ok(())
408 }
409}
410
411pub struct IsChildSystemParachain<ParaId>(PhantomData<ParaId>);
413impl<ParaId: IsSystem + From<u32>> Contains<Location> for IsChildSystemParachain<ParaId> {
414 fn contains(l: &Location) -> bool {
415 matches!(
416 l.interior().as_slice(),
417 [Junction::Parachain(id)]
418 if ParaId::from(*id).is_system() && l.parent_count() == 0,
419 )
420 }
421}
422
423pub struct IsSiblingSystemParachain<ParaId, SelfParaId>(PhantomData<(ParaId, SelfParaId)>);
425impl<ParaId: IsSystem + From<u32> + Eq, SelfParaId: Get<ParaId>> Contains<Location>
426 for IsSiblingSystemParachain<ParaId, SelfParaId>
427{
428 fn contains(l: &Location) -> bool {
429 matches!(
430 l.unpack(),
431 (1, [Junction::Parachain(id)])
432 if SelfParaId::get() != ParaId::from(*id) && ParaId::from(*id).is_system(),
433 )
434 }
435}
436
437pub struct IsParentsOnly<Count>(PhantomData<Count>);
440impl<Count: Get<u8>> Contains<Location> for IsParentsOnly<Count> {
441 fn contains(t: &Location) -> bool {
442 t.contains_parents_only(Count::get())
443 }
444}
445
446pub struct AllowKnownQueryResponses<ResponseHandler>(PhantomData<ResponseHandler>);
448impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<ResponseHandler> {
449 fn should_execute<RuntimeCall>(
450 origin: &Location,
451 instructions: &mut [Instruction<RuntimeCall>],
452 max_weight: Weight,
453 properties: &mut Properties,
454 ) -> Result<(), ProcessMessageError> {
455 tracing::trace!(
456 target: "xcm::barriers",
457 ?origin, ?instructions, ?max_weight, ?properties,
458 "AllowKnownQueryResponses"
459 );
460 instructions
461 .matcher()
462 .assert_remaining_insts(1)?
463 .match_next_inst(|inst| match inst {
464 QueryResponse { query_id, querier, .. }
465 if ResponseHandler::expecting_response(origin, *query_id, querier.as_ref()) =>
466 {
467 Ok(())
468 },
469 _ => Err(ProcessMessageError::BadFormat),
470 })?;
471 Ok(())
472 }
473}
474
475pub struct AllowSubscriptionsFrom<T>(PhantomData<T>);
478impl<T: Contains<Location>> ShouldExecute for AllowSubscriptionsFrom<T> {
479 fn should_execute<RuntimeCall>(
480 origin: &Location,
481 instructions: &mut [Instruction<RuntimeCall>],
482 max_weight: Weight,
483 properties: &mut Properties,
484 ) -> Result<(), ProcessMessageError> {
485 tracing::trace!(
486 target: "xcm::barriers",
487 ?origin, ?instructions, ?max_weight, ?properties,
488 "AllowSubscriptionsFrom",
489 );
490 ensure!(T::contains(origin), ProcessMessageError::Unsupported);
491 instructions
492 .matcher()
493 .assert_remaining_insts(1)?
494 .match_next_inst(|inst| match inst {
495 SubscribeVersion { .. } | UnsubscribeVersion => Ok(()),
496 _ => Err(ProcessMessageError::BadFormat),
497 })?;
498 Ok(())
499 }
500}
501
502pub struct AllowHrmpNotificationsFromRelayChain;
509impl ShouldExecute for AllowHrmpNotificationsFromRelayChain {
510 fn should_execute<RuntimeCall>(
511 origin: &Location,
512 instructions: &mut [Instruction<RuntimeCall>],
513 max_weight: Weight,
514 properties: &mut Properties,
515 ) -> Result<(), ProcessMessageError> {
516 tracing::trace!(
517 target: "xcm::barriers",
518 ?origin, ?instructions, ?max_weight, ?properties,
519 "AllowHrmpNotificationsFromRelayChain"
520 );
521 ensure!(matches!(origin.unpack(), (1, [])), ProcessMessageError::Unsupported);
523 instructions
525 .matcher()
526 .assert_remaining_insts(1)?
527 .match_next_inst(|inst| match inst {
528 HrmpNewChannelOpenRequest { .. } |
529 HrmpChannelAccepted { .. } |
530 HrmpChannelClosing { .. } => Ok(()),
531 _ => Err(ProcessMessageError::BadFormat),
532 })?;
533 Ok(())
534 }
535}
536
537pub struct DenyThenTry<Deny, Allow>(PhantomData<Deny>, PhantomData<Allow>)
540where
541 Deny: DenyExecution,
542 Allow: ShouldExecute;
543
544impl<Deny, Allow> ShouldExecute for DenyThenTry<Deny, Allow>
545where
546 Deny: DenyExecution,
547 Allow: ShouldExecute,
548{
549 fn should_execute<RuntimeCall>(
550 origin: &Location,
551 message: &mut [Instruction<RuntimeCall>],
552 max_weight: Weight,
553 properties: &mut Properties,
554 ) -> Result<(), ProcessMessageError> {
555 Deny::deny_execution(origin, message, max_weight, properties)?;
556 Allow::should_execute(origin, message, max_weight, properties)
557 }
558}
559
560pub struct DenyReserveTransferToRelayChain;
562impl DenyExecution for DenyReserveTransferToRelayChain {
563 fn deny_execution<RuntimeCall>(
564 origin: &Location,
565 message: &mut [Instruction<RuntimeCall>],
566 _max_weight: Weight,
567 _properties: &mut Properties,
568 ) -> Result<(), ProcessMessageError> {
569 message.matcher().match_next_inst_while(
570 |_| true,
571 |inst| match inst {
572 InitiateReserveWithdraw {
573 reserve: Location { parents: 1, interior: Here },
574 ..
575 } |
576 DepositReserveAsset { dest: Location { parents: 1, interior: Here }, .. } |
577 TransferReserveAsset { dest: Location { parents: 1, interior: Here }, .. } => {
578 Err(ProcessMessageError::Unsupported) },
580
581 ReserveAssetDeposited { .. }
584 if matches!(origin, Location { parents: 1, interior: Here }) =>
585 {
586 tracing::debug!(
587 target: "xcm::barriers",
588 "Unexpected ReserveAssetDeposited from the Relay Chain",
589 );
590 Ok(ControlFlow::Continue(()))
591 },
592
593 _ => Ok(ControlFlow::Continue(())),
594 },
595 )?;
596 Ok(())
597 }
598}
599
600environmental::environmental!(recursion_count: u8);
601
602pub struct DenyRecursively<Inner>(PhantomData<Inner>);
612
613impl<Inner: DenyExecution> DenyRecursively<Inner> {
614 fn deny_recursively<RuntimeCall>(
619 origin: &Location,
620 xcm: &mut Xcm<RuntimeCall>,
621 max_weight: Weight,
622 properties: &mut Properties,
623 ) -> Result<ControlFlow<()>, ProcessMessageError> {
624 recursion_count::using_once(&mut 1, || {
626 recursion_count::with(|count| {
628 if *count > xcm::RECURSION_LIMIT {
629 tracing::debug!(
630 target: "xcm::barriers",
631 "Recursion limit exceeded (count: {count}), origin: {:?}, xcm: {:?}, max_weight: {:?}, properties: {:?}",
632 origin, xcm, max_weight, properties
633 );
634 return None;
635 }
636 *count = count.saturating_add(1);
637 Some(())
638 }).flatten().ok_or(ProcessMessageError::StackLimitReached)?;
639
640 sp_core::defer! {
642 recursion_count::with(|count| {
643 *count = count.saturating_sub(1);
644 });
645 }
646
647 Self::deny_execution(origin, xcm.inner_mut(), max_weight, properties)
649 })?;
650
651 Ok(ControlFlow::Continue(()))
652 }
653}
654
655impl<Inner: DenyExecution> DenyExecution for DenyRecursively<Inner> {
656 fn deny_execution<RuntimeCall>(
661 origin: &Location,
662 instructions: &mut [Instruction<RuntimeCall>],
663 max_weight: Weight,
664 properties: &mut Properties,
665 ) -> Result<(), ProcessMessageError> {
666 Inner::deny_execution(origin, instructions, max_weight, properties).inspect_err(|e| {
668 tracing::debug!(
669 target: "xcm::barriers",
670 "DenyRecursively::Inner denied execution, origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}, error: {:?}",
671 origin, instructions, max_weight, properties, e
672 );
673 })?;
674
675 instructions.matcher().match_next_inst_while(
677 |_| true,
678 |inst| match inst {
679 SetAppendix(nested_xcm) |
680 SetErrorHandler(nested_xcm) |
681 ExecuteWithOrigin { xcm: nested_xcm, .. } => Self::deny_recursively::<RuntimeCall>(
682 origin, nested_xcm, max_weight, properties,
683 ),
684 _ => Ok(ControlFlow::Continue(())),
685 },
686 )?;
687
688 Ok(())
690 }
691}