1use crate::{CreateMatcher, MatchXcm};
20use core::{cell::Cell, marker::PhantomData, ops::ControlFlow, result::Result};
21use pezframe_support::{
22 ensure,
23 traits::{Contains, ContainsPair, Get, Nothing, ProcessMessageError},
24};
25use pezkuwi_teyrchain_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)>);
322impl<T: Contains<Location>, Aliasers: ContainsPair<Location, Location>> ShouldExecute
323 for AllowExplicitUnpaidExecutionFrom<T, Aliasers>
324{
325 fn should_execute<Call>(
326 origin: &Location,
327 instructions: &mut [Instruction<Call>],
328 max_weight: Weight,
329 properties: &mut Properties,
330 ) -> Result<(), ProcessMessageError> {
331 tracing::trace!(
332 target: "xcm::barriers",
333 ?origin, ?instructions, ?max_weight, ?properties,
334 "AllowExplicitUnpaidExecutionFrom",
335 );
336 let mut actual_origin = origin.clone();
340 let processed = Cell::new(0usize);
341 let instructions_to_process = 5;
342 instructions
343 .matcher()
344 .match_next_inst_while(
346 |inst| {
347 processed.get() < instructions_to_process
348 && matches!(
349 inst,
350 ReceiveTeleportedAsset(_)
351 | ReserveAssetDeposited(_)
352 | WithdrawAsset(_) | SetHints { .. }
353 )
354 },
355 |_| {
356 processed.set(processed.get() + 1);
357 Ok(ControlFlow::Continue(()))
358 },
359 )?
360 .match_next_inst_while(
363 |_| processed.get() < instructions_to_process,
364 |inst| {
365 match inst {
366 ClearOrigin => {
367 return Err(ProcessMessageError::Unsupported);
370 },
371 AliasOrigin(target) => {
372 if Aliasers::contains(&actual_origin, &target) {
373 actual_origin = target.clone();
374 } else {
375 return Err(ProcessMessageError::Unsupported);
376 }
377 },
378 DescendOrigin(child) if child != &Here => {
379 let Ok(_) = actual_origin.append_with(child.clone()) else {
380 return Err(ProcessMessageError::Unsupported);
381 };
382 },
383 _ => return Ok(ControlFlow::Break(())),
384 };
385 processed.set(processed.get() + 1);
386 Ok(ControlFlow::Continue(()))
387 },
388 )?
389 .match_next_inst(|inst| match inst {
391 UnpaidExecution { weight_limit: Limited(m), .. } if m.all_gte(max_weight) => Ok(()),
392 UnpaidExecution { weight_limit: Unlimited, .. } => Ok(()),
393 _ => Err(ProcessMessageError::Overweight(max_weight)),
394 })?;
395
396 ensure!(T::contains(&actual_origin), ProcessMessageError::Unsupported);
399
400 Ok(())
401 }
402}
403
404pub struct IsChildSystemTeyrchain<ParaId>(PhantomData<ParaId>);
406impl<ParaId: IsSystem + From<u32>> Contains<Location> for IsChildSystemTeyrchain<ParaId> {
407 fn contains(l: &Location) -> bool {
408 matches!(
409 l.interior().as_slice(),
410 [Junction::Teyrchain(id)]
411 if ParaId::from(*id).is_system() && l.parent_count() == 0,
412 )
413 }
414}
415
416pub struct IsSiblingSystemTeyrchain<ParaId, SelfParaId>(PhantomData<(ParaId, SelfParaId)>);
418impl<ParaId: IsSystem + From<u32> + Eq, SelfParaId: Get<ParaId>> Contains<Location>
419 for IsSiblingSystemTeyrchain<ParaId, SelfParaId>
420{
421 fn contains(l: &Location) -> bool {
422 matches!(
423 l.unpack(),
424 (1, [Junction::Teyrchain(id)])
425 if SelfParaId::get() != ParaId::from(*id) && ParaId::from(*id).is_system(),
426 )
427 }
428}
429
430pub struct IsParentsOnly<Count>(PhantomData<Count>);
433impl<Count: Get<u8>> Contains<Location> for IsParentsOnly<Count> {
434 fn contains(t: &Location) -> bool {
435 t.contains_parents_only(Count::get())
436 }
437}
438
439pub struct AllowKnownQueryResponses<ResponseHandler>(PhantomData<ResponseHandler>);
441impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<ResponseHandler> {
442 fn should_execute<RuntimeCall>(
443 origin: &Location,
444 instructions: &mut [Instruction<RuntimeCall>],
445 max_weight: Weight,
446 properties: &mut Properties,
447 ) -> Result<(), ProcessMessageError> {
448 tracing::trace!(
449 target: "xcm::barriers",
450 ?origin, ?instructions, ?max_weight, ?properties,
451 "AllowKnownQueryResponses"
452 );
453 instructions
454 .matcher()
455 .assert_remaining_insts(1)?
456 .match_next_inst(|inst| match inst {
457 QueryResponse { query_id, querier, .. }
458 if ResponseHandler::expecting_response(origin, *query_id, querier.as_ref()) =>
459 {
460 Ok(())
461 },
462 _ => Err(ProcessMessageError::BadFormat),
463 })?;
464 Ok(())
465 }
466}
467
468pub struct AllowSubscriptionsFrom<T>(PhantomData<T>);
471impl<T: Contains<Location>> ShouldExecute for AllowSubscriptionsFrom<T> {
472 fn should_execute<RuntimeCall>(
473 origin: &Location,
474 instructions: &mut [Instruction<RuntimeCall>],
475 max_weight: Weight,
476 properties: &mut Properties,
477 ) -> Result<(), ProcessMessageError> {
478 tracing::trace!(
479 target: "xcm::barriers",
480 ?origin, ?instructions, ?max_weight, ?properties,
481 "AllowSubscriptionsFrom",
482 );
483 ensure!(T::contains(origin), ProcessMessageError::Unsupported);
484 instructions
485 .matcher()
486 .assert_remaining_insts(1)?
487 .match_next_inst(|inst| match inst {
488 SubscribeVersion { .. } | UnsubscribeVersion => Ok(()),
489 _ => Err(ProcessMessageError::BadFormat),
490 })?;
491 Ok(())
492 }
493}
494
495pub struct AllowHrmpNotificationsFromRelayChain;
502impl ShouldExecute for AllowHrmpNotificationsFromRelayChain {
503 fn should_execute<RuntimeCall>(
504 origin: &Location,
505 instructions: &mut [Instruction<RuntimeCall>],
506 max_weight: Weight,
507 properties: &mut Properties,
508 ) -> Result<(), ProcessMessageError> {
509 tracing::trace!(
510 target: "xcm::barriers",
511 ?origin, ?instructions, ?max_weight, ?properties,
512 "AllowHrmpNotificationsFromRelayChain"
513 );
514 ensure!(matches!(origin.unpack(), (1, [])), ProcessMessageError::Unsupported);
516 instructions
518 .matcher()
519 .assert_remaining_insts(1)?
520 .match_next_inst(|inst| match inst {
521 HrmpNewChannelOpenRequest { .. }
522 | HrmpChannelAccepted { .. }
523 | HrmpChannelClosing { .. } => Ok(()),
524 _ => Err(ProcessMessageError::BadFormat),
525 })?;
526 Ok(())
527 }
528}
529
530pub struct DenyThenTry<Deny, Allow>(PhantomData<Deny>, PhantomData<Allow>)
533where
534 Deny: DenyExecution,
535 Allow: ShouldExecute;
536
537impl<Deny, Allow> ShouldExecute for DenyThenTry<Deny, Allow>
538where
539 Deny: DenyExecution,
540 Allow: ShouldExecute,
541{
542 fn should_execute<RuntimeCall>(
543 origin: &Location,
544 message: &mut [Instruction<RuntimeCall>],
545 max_weight: Weight,
546 properties: &mut Properties,
547 ) -> Result<(), ProcessMessageError> {
548 Deny::deny_execution(origin, message, max_weight, properties)?;
549 Allow::should_execute(origin, message, max_weight, properties)
550 }
551}
552
553pub struct DenyReserveTransferToRelayChain;
555impl DenyExecution for DenyReserveTransferToRelayChain {
556 fn deny_execution<RuntimeCall>(
557 origin: &Location,
558 message: &mut [Instruction<RuntimeCall>],
559 _max_weight: Weight,
560 _properties: &mut Properties,
561 ) -> Result<(), ProcessMessageError> {
562 message.matcher().match_next_inst_while(
563 |_| true,
564 |inst| match inst {
565 InitiateReserveWithdraw {
566 reserve: Location { parents: 1, interior: Here },
567 ..
568 }
569 | DepositReserveAsset { dest: Location { parents: 1, interior: Here }, .. }
570 | TransferReserveAsset { dest: Location { parents: 1, interior: Here }, .. } => {
571 Err(ProcessMessageError::Unsupported) },
573
574 ReserveAssetDeposited { .. }
577 if matches!(origin, Location { parents: 1, interior: Here }) =>
578 {
579 tracing::debug!(
580 target: "xcm::barriers",
581 "Unexpected ReserveAssetDeposited from the Relay Chain",
582 );
583 Ok(ControlFlow::Continue(()))
584 },
585
586 _ => Ok(ControlFlow::Continue(())),
587 },
588 )?;
589 Ok(())
590 }
591}
592
593environmental::environmental!(recursion_count: u8);
594
595pub struct DenyRecursively<Inner>(PhantomData<Inner>);
605
606impl<Inner: DenyExecution> DenyRecursively<Inner> {
607 fn deny_recursively<RuntimeCall>(
612 origin: &Location,
613 xcm: &mut Xcm<RuntimeCall>,
614 max_weight: Weight,
615 properties: &mut Properties,
616 ) -> Result<ControlFlow<()>, ProcessMessageError> {
617 recursion_count::using_once(&mut 1, || {
619 recursion_count::with(|count| {
621 if *count > xcm_executor::RECURSION_LIMIT {
622 tracing::debug!(
623 target: "xcm::barriers",
624 "Recursion limit exceeded (count: {count}), origin: {:?}, xcm: {:?}, max_weight: {:?}, properties: {:?}",
625 origin, xcm, max_weight, properties
626 );
627 return None;
628 }
629 *count = count.saturating_add(1);
630 Some(())
631 }).flatten().ok_or(ProcessMessageError::StackLimitReached)?;
632
633 pezsp_core::defer! {
635 recursion_count::with(|count| {
636 *count = count.saturating_sub(1);
637 });
638 }
639
640 Self::deny_execution(origin, xcm.inner_mut(), max_weight, properties)
642 })?;
643
644 Ok(ControlFlow::Continue(()))
645 }
646}
647
648impl<Inner: DenyExecution> DenyExecution for DenyRecursively<Inner> {
649 fn deny_execution<RuntimeCall>(
654 origin: &Location,
655 instructions: &mut [Instruction<RuntimeCall>],
656 max_weight: Weight,
657 properties: &mut Properties,
658 ) -> Result<(), ProcessMessageError> {
659 Inner::deny_execution(origin, instructions, max_weight, properties).inspect_err(|e| {
661 tracing::debug!(
662 target: "xcm::barriers",
663 "DenyRecursively::Inner denied execution, origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}, error: {:?}",
664 origin, instructions, max_weight, properties, e
665 );
666 })?;
667
668 instructions.matcher().match_next_inst_while(
670 |_| true,
671 |inst| match inst {
672 SetAppendix(nested_xcm)
673 | SetErrorHandler(nested_xcm)
674 | ExecuteWithOrigin { xcm: nested_xcm, .. } => Self::deny_recursively::<RuntimeCall>(
675 origin, nested_xcm, max_weight, properties,
676 ),
677 _ => Ok(ControlFlow::Continue(())),
678 },
679 )?;
680
681 Ok(())
683 }
684}