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 _ => Err(ProcessMessageError::BadFormat),
101 })?
102 .skip_inst_while(|inst| {
103 matches!(inst, ClearOrigin | AliasOrigin(..)) ||
104 matches!(inst, DescendOrigin(child) if child != &Here) ||
105 matches!(inst, SetHints { .. })
106 })?
107 .match_next_inst(|inst| match inst {
108 BuyExecution { weight_limit: Limited(ref mut weight), .. }
109 if weight.all_gte(max_weight) =>
110 {
111 *weight = max_weight;
112 Ok(())
113 },
114 BuyExecution { ref mut weight_limit, .. } if weight_limit == &Unlimited => {
115 *weight_limit = Limited(max_weight);
116 Ok(())
117 },
118 PayFees { .. } => Ok(()),
119 _ => Err(ProcessMessageError::Overweight(max_weight)),
120 })?;
121 Ok(())
122 }
123}
124
125pub struct WithComputedOrigin<InnerBarrier, LocalUniversal, MaxPrefixes>(
171 PhantomData<(InnerBarrier, LocalUniversal, MaxPrefixes)>,
172);
173impl<InnerBarrier: ShouldExecute, LocalUniversal: Get<InteriorLocation>, MaxPrefixes: Get<u32>>
174 ShouldExecute for WithComputedOrigin<InnerBarrier, LocalUniversal, MaxPrefixes>
175{
176 fn should_execute<Call>(
177 origin: &Location,
178 instructions: &mut [Instruction<Call>],
179 max_weight: Weight,
180 properties: &mut Properties,
181 ) -> Result<(), ProcessMessageError> {
182 tracing::trace!(
183 target: "xcm::barriers",
184 ?origin,
185 ?instructions,
186 ?max_weight,
187 ?properties,
188 "WithComputedOrigin"
189 );
190 let mut actual_origin = origin.clone();
191 let skipped = Cell::new(0usize);
192 instructions.matcher().match_next_inst_while(
198 |_| skipped.get() < MaxPrefixes::get() as usize,
199 |inst| {
200 match inst {
201 UniversalOrigin(new_global) => {
202 actual_origin =
206 Junctions::from([*new_global]).relative_to(&LocalUniversal::get());
207 },
208 DescendOrigin(j) => {
209 let Ok(_) = actual_origin.append_with(j.clone()) else {
210 return Err(ProcessMessageError::Unsupported)
211 };
212 },
213 _ => return Ok(ControlFlow::Break(())),
214 };
215 skipped.set(skipped.get() + 1);
216 Ok(ControlFlow::Continue(()))
217 },
218 )?;
219 InnerBarrier::should_execute(
220 &actual_origin,
221 &mut instructions[skipped.get()..],
222 max_weight,
223 properties,
224 )
225 }
226}
227
228pub struct TrailingSetTopicAsId<InnerBarrier>(PhantomData<InnerBarrier>);
235impl<InnerBarrier: ShouldExecute> ShouldExecute for TrailingSetTopicAsId<InnerBarrier> {
236 fn should_execute<Call>(
237 origin: &Location,
238 instructions: &mut [Instruction<Call>],
239 max_weight: Weight,
240 properties: &mut Properties,
241 ) -> Result<(), ProcessMessageError> {
242 tracing::trace!(
243 target: "xcm::barriers",
244 ?origin,
245 ?instructions,
246 ?max_weight,
247 ?properties,
248 "TrailingSetTopicAsId"
249 );
250 let until = if let Some(SetTopic(t)) = instructions.last() {
251 properties.message_id = Some(*t);
252 instructions.len() - 1
253 } else {
254 instructions.len()
255 };
256 InnerBarrier::should_execute(&origin, &mut instructions[..until], max_weight, properties)
257 }
258}
259
260pub struct RespectSuspension<Inner, SuspensionChecker>(PhantomData<(Inner, SuspensionChecker)>);
263impl<Inner, SuspensionChecker> ShouldExecute for RespectSuspension<Inner, SuspensionChecker>
264where
265 Inner: ShouldExecute,
266 SuspensionChecker: CheckSuspension,
267{
268 fn should_execute<Call>(
269 origin: &Location,
270 instructions: &mut [Instruction<Call>],
271 max_weight: Weight,
272 properties: &mut Properties,
273 ) -> Result<(), ProcessMessageError> {
274 if SuspensionChecker::is_suspended(origin, instructions, max_weight, properties) {
275 Err(ProcessMessageError::Yield)
276 } else {
277 Inner::should_execute(origin, instructions, max_weight, properties)
278 }
279 }
280}
281
282pub struct AllowUnpaidExecutionFrom<T>(PhantomData<T>);
287impl<T: Contains<Location>> ShouldExecute for AllowUnpaidExecutionFrom<T> {
288 fn should_execute<RuntimeCall>(
289 origin: &Location,
290 instructions: &mut [Instruction<RuntimeCall>],
291 max_weight: Weight,
292 properties: &mut Properties,
293 ) -> Result<(), ProcessMessageError> {
294 tracing::trace!(
295 target: "xcm::barriers",
296 ?origin, ?instructions, ?max_weight, ?properties,
297 "AllowUnpaidExecutionFrom"
298 );
299 ensure!(T::contains(origin), ProcessMessageError::Unsupported);
300 Ok(())
301 }
302}
303
304pub struct AllowExplicitUnpaidExecutionFrom<T, Aliasers = Nothing>(PhantomData<(T, Aliasers)>);
321impl<T: Contains<Location>, Aliasers: ContainsPair<Location, Location>> ShouldExecute
322 for AllowExplicitUnpaidExecutionFrom<T, Aliasers>
323{
324 fn should_execute<Call>(
325 origin: &Location,
326 instructions: &mut [Instruction<Call>],
327 max_weight: Weight,
328 properties: &mut Properties,
329 ) -> Result<(), ProcessMessageError> {
330 tracing::trace!(
331 target: "xcm::barriers",
332 ?origin, ?instructions, ?max_weight, ?properties,
333 "AllowExplicitUnpaidExecutionFrom",
334 );
335 let mut actual_origin = origin.clone();
339 let processed = Cell::new(0usize);
340 let instructions_to_process = 5;
341 instructions
342 .matcher()
343 .match_next_inst_while(
345 |inst| {
346 processed.get() < instructions_to_process &&
347 matches!(
348 inst,
349 ReceiveTeleportedAsset(_) |
350 ReserveAssetDeposited(_) | WithdrawAsset(_) |
351 SetHints { .. }
352 )
353 },
354 |_| {
355 processed.set(processed.get() + 1);
356 Ok(ControlFlow::Continue(()))
357 },
358 )?
359 .match_next_inst_while(
362 |_| processed.get() < instructions_to_process,
363 |inst| {
364 match inst {
365 ClearOrigin => {
366 return Err(ProcessMessageError::Unsupported);
369 },
370 AliasOrigin(target) =>
371 if Aliasers::contains(&actual_origin, &target) {
372 actual_origin = target.clone();
373 } else {
374 return Err(ProcessMessageError::Unsupported);
375 },
376 DescendOrigin(child) if child != &Here => {
377 let Ok(_) = actual_origin.append_with(child.clone()) else {
378 return Err(ProcessMessageError::Unsupported);
379 };
380 },
381 _ => return Ok(ControlFlow::Break(())),
382 };
383 processed.set(processed.get() + 1);
384 Ok(ControlFlow::Continue(()))
385 },
386 )?
387 .match_next_inst(|inst| match inst {
389 UnpaidExecution { weight_limit: Limited(m), .. } if m.all_gte(max_weight) => Ok(()),
390 UnpaidExecution { weight_limit: Unlimited, .. } => Ok(()),
391 _ => Err(ProcessMessageError::Overweight(max_weight)),
392 })?;
393
394 ensure!(T::contains(&actual_origin), ProcessMessageError::Unsupported);
397
398 Ok(())
399 }
400}
401
402pub struct IsChildSystemParachain<ParaId>(PhantomData<ParaId>);
404impl<ParaId: IsSystem + From<u32>> Contains<Location> for IsChildSystemParachain<ParaId> {
405 fn contains(l: &Location) -> bool {
406 matches!(
407 l.interior().as_slice(),
408 [Junction::Parachain(id)]
409 if ParaId::from(*id).is_system() && l.parent_count() == 0,
410 )
411 }
412}
413
414pub struct IsSiblingSystemParachain<ParaId, SelfParaId>(PhantomData<(ParaId, SelfParaId)>);
416impl<ParaId: IsSystem + From<u32> + Eq, SelfParaId: Get<ParaId>> Contains<Location>
417 for IsSiblingSystemParachain<ParaId, SelfParaId>
418{
419 fn contains(l: &Location) -> bool {
420 matches!(
421 l.unpack(),
422 (1, [Junction::Parachain(id)])
423 if SelfParaId::get() != ParaId::from(*id) && ParaId::from(*id).is_system(),
424 )
425 }
426}
427
428pub struct IsParentsOnly<Count>(PhantomData<Count>);
431impl<Count: Get<u8>> Contains<Location> for IsParentsOnly<Count> {
432 fn contains(t: &Location) -> bool {
433 t.contains_parents_only(Count::get())
434 }
435}
436
437pub struct AllowKnownQueryResponses<ResponseHandler>(PhantomData<ResponseHandler>);
439impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<ResponseHandler> {
440 fn should_execute<RuntimeCall>(
441 origin: &Location,
442 instructions: &mut [Instruction<RuntimeCall>],
443 max_weight: Weight,
444 properties: &mut Properties,
445 ) -> Result<(), ProcessMessageError> {
446 tracing::trace!(
447 target: "xcm::barriers",
448 ?origin, ?instructions, ?max_weight, ?properties,
449 "AllowKnownQueryResponses"
450 );
451 instructions
452 .matcher()
453 .assert_remaining_insts(1)?
454 .match_next_inst(|inst| match inst {
455 QueryResponse { query_id, querier, .. }
456 if ResponseHandler::expecting_response(origin, *query_id, querier.as_ref()) =>
457 Ok(()),
458 _ => Err(ProcessMessageError::BadFormat),
459 })?;
460 Ok(())
461 }
462}
463
464pub struct AllowSubscriptionsFrom<T>(PhantomData<T>);
467impl<T: Contains<Location>> ShouldExecute for AllowSubscriptionsFrom<T> {
468 fn should_execute<RuntimeCall>(
469 origin: &Location,
470 instructions: &mut [Instruction<RuntimeCall>],
471 max_weight: Weight,
472 properties: &mut Properties,
473 ) -> Result<(), ProcessMessageError> {
474 tracing::trace!(
475 target: "xcm::barriers",
476 ?origin, ?instructions, ?max_weight, ?properties,
477 "AllowSubscriptionsFrom",
478 );
479 ensure!(T::contains(origin), ProcessMessageError::Unsupported);
480 instructions
481 .matcher()
482 .assert_remaining_insts(1)?
483 .match_next_inst(|inst| match inst {
484 SubscribeVersion { .. } | UnsubscribeVersion => Ok(()),
485 _ => Err(ProcessMessageError::BadFormat),
486 })?;
487 Ok(())
488 }
489}
490
491pub struct AllowHrmpNotificationsFromRelayChain;
498impl ShouldExecute for AllowHrmpNotificationsFromRelayChain {
499 fn should_execute<RuntimeCall>(
500 origin: &Location,
501 instructions: &mut [Instruction<RuntimeCall>],
502 max_weight: Weight,
503 properties: &mut Properties,
504 ) -> Result<(), ProcessMessageError> {
505 tracing::trace!(
506 target: "xcm::barriers",
507 ?origin, ?instructions, ?max_weight, ?properties,
508 "AllowHrmpNotificationsFromRelayChain"
509 );
510 ensure!(matches!(origin.unpack(), (1, [])), ProcessMessageError::Unsupported);
512 instructions
514 .matcher()
515 .assert_remaining_insts(1)?
516 .match_next_inst(|inst| match inst {
517 HrmpNewChannelOpenRequest { .. } |
518 HrmpChannelAccepted { .. } |
519 HrmpChannelClosing { .. } => Ok(()),
520 _ => Err(ProcessMessageError::BadFormat),
521 })?;
522 Ok(())
523 }
524}
525
526pub struct DenyThenTry<Deny, Allow>(PhantomData<Deny>, PhantomData<Allow>)
529where
530 Deny: DenyExecution,
531 Allow: ShouldExecute;
532
533impl<Deny, Allow> ShouldExecute for DenyThenTry<Deny, Allow>
534where
535 Deny: DenyExecution,
536 Allow: ShouldExecute,
537{
538 fn should_execute<RuntimeCall>(
539 origin: &Location,
540 message: &mut [Instruction<RuntimeCall>],
541 max_weight: Weight,
542 properties: &mut Properties,
543 ) -> Result<(), ProcessMessageError> {
544 Deny::deny_execution(origin, message, max_weight, properties)?;
545 Allow::should_execute(origin, message, max_weight, properties)
546 }
547}
548
549pub struct DenyReserveTransferToRelayChain;
551impl DenyExecution for DenyReserveTransferToRelayChain {
552 fn deny_execution<RuntimeCall>(
553 origin: &Location,
554 message: &mut [Instruction<RuntimeCall>],
555 _max_weight: Weight,
556 _properties: &mut Properties,
557 ) -> Result<(), ProcessMessageError> {
558 message.matcher().match_next_inst_while(
559 |_| true,
560 |inst| match inst {
561 InitiateReserveWithdraw {
562 reserve: Location { parents: 1, interior: Here },
563 ..
564 } |
565 DepositReserveAsset { dest: Location { parents: 1, interior: Here }, .. } |
566 TransferReserveAsset { dest: Location { parents: 1, interior: Here }, .. } => {
567 Err(ProcessMessageError::Unsupported) },
569
570 ReserveAssetDeposited { .. }
573 if matches!(origin, Location { parents: 1, interior: Here }) =>
574 {
575 tracing::debug!(
576 target: "xcm::barriers",
577 "Unexpected ReserveAssetDeposited from the Relay Chain",
578 );
579 Ok(ControlFlow::Continue(()))
580 },
581
582 _ => Ok(ControlFlow::Continue(())),
583 },
584 )?;
585 Ok(())
586 }
587}
588
589environmental::environmental!(recursion_count: u8);
590
591pub struct DenyRecursively<Inner>(PhantomData<Inner>);
601
602impl<Inner: DenyExecution> DenyRecursively<Inner> {
603 fn deny_recursively<RuntimeCall>(
608 origin: &Location,
609 xcm: &mut Xcm<RuntimeCall>,
610 max_weight: Weight,
611 properties: &mut Properties,
612 ) -> Result<ControlFlow<()>, ProcessMessageError> {
613 recursion_count::using_once(&mut 1, || {
615 recursion_count::with(|count| {
617 if *count > xcm_executor::RECURSION_LIMIT {
618 tracing::debug!(
619 target: "xcm::barriers",
620 "Recursion limit exceeded (count: {count}), origin: {:?}, xcm: {:?}, max_weight: {:?}, properties: {:?}",
621 origin, xcm, max_weight, properties
622 );
623 return None;
624 }
625 *count = count.saturating_add(1);
626 Some(())
627 }).flatten().ok_or(ProcessMessageError::StackLimitReached)?;
628
629 sp_core::defer! {
631 recursion_count::with(|count| {
632 *count = count.saturating_sub(1);
633 });
634 }
635
636 Self::deny_execution(origin, xcm.inner_mut(), max_weight, properties)
638 })?;
639
640 Ok(ControlFlow::Continue(()))
641 }
642}
643
644impl<Inner: DenyExecution> DenyExecution for DenyRecursively<Inner> {
645 fn deny_execution<RuntimeCall>(
650 origin: &Location,
651 instructions: &mut [Instruction<RuntimeCall>],
652 max_weight: Weight,
653 properties: &mut Properties,
654 ) -> Result<(), ProcessMessageError> {
655 Inner::deny_execution(origin, instructions, max_weight, properties).inspect_err(|e| {
657 tracing::debug!(
658 target: "xcm::barriers",
659 "DenyRecursively::Inner denied execution, origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}, error: {:?}",
660 origin, instructions, max_weight, properties, e
661 );
662 })?;
663
664 instructions.matcher().match_next_inst_while(
666 |_| true,
667 |inst| match inst {
668 SetAppendix(nested_xcm) |
669 SetErrorHandler(nested_xcm) |
670 ExecuteWithOrigin { xcm: nested_xcm, .. } => Self::deny_recursively::<RuntimeCall>(
671 origin, nested_xcm, max_weight, properties,
672 ),
673 _ => Ok(ControlFlow::Continue(())),
674 },
675 )?;
676
677 Ok(())
679 }
680}