use crate::{CreateMatcher, MatchXcm};
use core::{cell::Cell, marker::PhantomData, ops::ControlFlow, result::Result};
use frame_support::{
ensure,
traits::{Contains, Get, ProcessMessageError},
};
use polkadot_parachain_primitives::primitives::IsSystem;
use xcm::prelude::*;
use xcm_executor::traits::{CheckSuspension, OnResponse, Properties, ShouldExecute};
pub struct TakeWeightCredit;
impl ShouldExecute for TakeWeightCredit {
fn should_execute<RuntimeCall>(
_origin: &Location,
_instructions: &mut [Instruction<RuntimeCall>],
max_weight: Weight,
properties: &mut Properties,
) -> Result<(), ProcessMessageError> {
log::trace!(
target: "xcm::barriers",
"TakeWeightCredit origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
_origin, _instructions, max_weight, properties,
);
properties.weight_credit = properties
.weight_credit
.checked_sub(&max_weight)
.ok_or(ProcessMessageError::Overweight(max_weight))?;
Ok(())
}
}
const MAX_ASSETS_FOR_BUY_EXECUTION: usize = 2;
pub struct AllowTopLevelPaidExecutionFrom<T>(PhantomData<T>);
impl<T: Contains<Location>> ShouldExecute for AllowTopLevelPaidExecutionFrom<T> {
fn should_execute<RuntimeCall>(
origin: &Location,
instructions: &mut [Instruction<RuntimeCall>],
max_weight: Weight,
_properties: &mut Properties,
) -> Result<(), ProcessMessageError> {
log::trace!(
target: "xcm::barriers",
"AllowTopLevelPaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
origin, instructions, max_weight, _properties,
);
ensure!(T::contains(origin), ProcessMessageError::Unsupported);
let end = instructions.len().min(5);
instructions[..end]
.matcher()
.match_next_inst(|inst| match inst {
WithdrawAsset(ref assets) |
ReceiveTeleportedAsset(ref assets) |
ReserveAssetDeposited(ref assets) |
ClaimAsset { ref assets, .. } =>
if assets.len() <= MAX_ASSETS_FOR_BUY_EXECUTION {
Ok(())
} else {
Err(ProcessMessageError::BadFormat)
},
_ => Err(ProcessMessageError::BadFormat),
})?
.skip_inst_while(|inst| {
matches!(inst, ClearOrigin | AliasOrigin(..)) ||
matches!(inst, DescendOrigin(child) if child != &Here)
})?
.match_next_inst(|inst| match inst {
BuyExecution { weight_limit: Limited(ref mut weight), .. }
if weight.all_gte(max_weight) =>
{
*weight = max_weight;
Ok(())
},
BuyExecution { ref mut weight_limit, .. } if weight_limit == &Unlimited => {
*weight_limit = Limited(max_weight);
Ok(())
},
PayFees { .. } => Ok(()),
_ => Err(ProcessMessageError::Overweight(max_weight)),
})?;
Ok(())
}
}
pub struct WithComputedOrigin<InnerBarrier, LocalUniversal, MaxPrefixes>(
PhantomData<(InnerBarrier, LocalUniversal, MaxPrefixes)>,
);
impl<InnerBarrier: ShouldExecute, LocalUniversal: Get<InteriorLocation>, MaxPrefixes: Get<u32>>
ShouldExecute for WithComputedOrigin<InnerBarrier, LocalUniversal, MaxPrefixes>
{
fn should_execute<Call>(
origin: &Location,
instructions: &mut [Instruction<Call>],
max_weight: Weight,
properties: &mut Properties,
) -> Result<(), ProcessMessageError> {
log::trace!(
target: "xcm::barriers",
"WithComputedOrigin origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
origin, instructions, max_weight, properties,
);
let mut actual_origin = origin.clone();
let skipped = Cell::new(0usize);
instructions.matcher().match_next_inst_while(
|_| skipped.get() < MaxPrefixes::get() as usize,
|inst| {
match inst {
UniversalOrigin(new_global) => {
actual_origin =
Junctions::from([*new_global]).relative_to(&LocalUniversal::get());
},
DescendOrigin(j) => {
let Ok(_) = actual_origin.append_with(j.clone()) else {
return Err(ProcessMessageError::Unsupported)
};
},
_ => return Ok(ControlFlow::Break(())),
};
skipped.set(skipped.get() + 1);
Ok(ControlFlow::Continue(()))
},
)?;
InnerBarrier::should_execute(
&actual_origin,
&mut instructions[skipped.get()..],
max_weight,
properties,
)
}
}
pub struct TrailingSetTopicAsId<InnerBarrier>(PhantomData<InnerBarrier>);
impl<InnerBarrier: ShouldExecute> ShouldExecute for TrailingSetTopicAsId<InnerBarrier> {
fn should_execute<Call>(
origin: &Location,
instructions: &mut [Instruction<Call>],
max_weight: Weight,
properties: &mut Properties,
) -> Result<(), ProcessMessageError> {
log::trace!(
target: "xcm::barriers",
"TrailingSetTopicAsId origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
origin, instructions, max_weight, properties,
);
let until = if let Some(SetTopic(t)) = instructions.last() {
properties.message_id = Some(*t);
instructions.len() - 1
} else {
instructions.len()
};
InnerBarrier::should_execute(&origin, &mut instructions[..until], max_weight, properties)
}
}
pub struct RespectSuspension<Inner, SuspensionChecker>(PhantomData<(Inner, SuspensionChecker)>);
impl<Inner, SuspensionChecker> ShouldExecute for RespectSuspension<Inner, SuspensionChecker>
where
Inner: ShouldExecute,
SuspensionChecker: CheckSuspension,
{
fn should_execute<Call>(
origin: &Location,
instructions: &mut [Instruction<Call>],
max_weight: Weight,
properties: &mut Properties,
) -> Result<(), ProcessMessageError> {
if SuspensionChecker::is_suspended(origin, instructions, max_weight, properties) {
Err(ProcessMessageError::Yield)
} else {
Inner::should_execute(origin, instructions, max_weight, properties)
}
}
}
pub struct AllowUnpaidExecutionFrom<T>(PhantomData<T>);
impl<T: Contains<Location>> ShouldExecute for AllowUnpaidExecutionFrom<T> {
fn should_execute<RuntimeCall>(
origin: &Location,
instructions: &mut [Instruction<RuntimeCall>],
_max_weight: Weight,
_properties: &mut Properties,
) -> Result<(), ProcessMessageError> {
log::trace!(
target: "xcm::barriers",
"AllowUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
origin, instructions, _max_weight, _properties,
);
ensure!(T::contains(origin), ProcessMessageError::Unsupported);
Ok(())
}
}
pub struct AllowExplicitUnpaidExecutionFrom<T>(PhantomData<T>);
impl<T: Contains<Location>> ShouldExecute for AllowExplicitUnpaidExecutionFrom<T> {
fn should_execute<Call>(
origin: &Location,
instructions: &mut [Instruction<Call>],
max_weight: Weight,
_properties: &mut Properties,
) -> Result<(), ProcessMessageError> {
log::trace!(
target: "xcm::barriers",
"AllowExplicitUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
origin, instructions, max_weight, _properties,
);
ensure!(T::contains(origin), ProcessMessageError::Unsupported);
instructions.matcher().match_next_inst(|inst| match inst {
UnpaidExecution { weight_limit: Limited(m), .. } if m.all_gte(max_weight) => Ok(()),
UnpaidExecution { weight_limit: Unlimited, .. } => Ok(()),
_ => Err(ProcessMessageError::Overweight(max_weight)),
})?;
Ok(())
}
}
pub struct IsChildSystemParachain<ParaId>(PhantomData<ParaId>);
impl<ParaId: IsSystem + From<u32>> Contains<Location> for IsChildSystemParachain<ParaId> {
fn contains(l: &Location) -> bool {
matches!(
l.interior().as_slice(),
[Junction::Parachain(id)]
if ParaId::from(*id).is_system() && l.parent_count() == 0,
)
}
}
pub struct IsSiblingSystemParachain<ParaId, SelfParaId>(PhantomData<(ParaId, SelfParaId)>);
impl<ParaId: IsSystem + From<u32> + Eq, SelfParaId: Get<ParaId>> Contains<Location>
for IsSiblingSystemParachain<ParaId, SelfParaId>
{
fn contains(l: &Location) -> bool {
matches!(
l.unpack(),
(1, [Junction::Parachain(id)])
if SelfParaId::get() != ParaId::from(*id) && ParaId::from(*id).is_system(),
)
}
}
pub struct IsParentsOnly<Count>(PhantomData<Count>);
impl<Count: Get<u8>> Contains<Location> for IsParentsOnly<Count> {
fn contains(t: &Location) -> bool {
t.contains_parents_only(Count::get())
}
}
pub struct AllowKnownQueryResponses<ResponseHandler>(PhantomData<ResponseHandler>);
impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<ResponseHandler> {
fn should_execute<RuntimeCall>(
origin: &Location,
instructions: &mut [Instruction<RuntimeCall>],
_max_weight: Weight,
_properties: &mut Properties,
) -> Result<(), ProcessMessageError> {
log::trace!(
target: "xcm::barriers",
"AllowKnownQueryResponses origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
origin, instructions, _max_weight, _properties,
);
instructions
.matcher()
.assert_remaining_insts(1)?
.match_next_inst(|inst| match inst {
QueryResponse { query_id, querier, .. }
if ResponseHandler::expecting_response(origin, *query_id, querier.as_ref()) =>
Ok(()),
_ => Err(ProcessMessageError::BadFormat),
})?;
Ok(())
}
}
pub struct AllowSubscriptionsFrom<T>(PhantomData<T>);
impl<T: Contains<Location>> ShouldExecute for AllowSubscriptionsFrom<T> {
fn should_execute<RuntimeCall>(
origin: &Location,
instructions: &mut [Instruction<RuntimeCall>],
_max_weight: Weight,
_properties: &mut Properties,
) -> Result<(), ProcessMessageError> {
log::trace!(
target: "xcm::barriers",
"AllowSubscriptionsFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
origin, instructions, _max_weight, _properties,
);
ensure!(T::contains(origin), ProcessMessageError::Unsupported);
instructions
.matcher()
.assert_remaining_insts(1)?
.match_next_inst(|inst| match inst {
SubscribeVersion { .. } | UnsubscribeVersion => Ok(()),
_ => Err(ProcessMessageError::BadFormat),
})?;
Ok(())
}
}
pub struct AllowHrmpNotificationsFromRelayChain;
impl ShouldExecute for AllowHrmpNotificationsFromRelayChain {
fn should_execute<RuntimeCall>(
origin: &Location,
instructions: &mut [Instruction<RuntimeCall>],
_max_weight: Weight,
_properties: &mut Properties,
) -> Result<(), ProcessMessageError> {
log::trace!(
target: "xcm::barriers",
"AllowHrmpNotificationsFromRelayChain origin: {:?}, instructions: {:?}, max_weight: {:?}, properties: {:?}",
origin, instructions, _max_weight, _properties,
);
ensure!(matches!(origin.unpack(), (1, [])), ProcessMessageError::Unsupported);
instructions
.matcher()
.assert_remaining_insts(1)?
.match_next_inst(|inst| match inst {
HrmpNewChannelOpenRequest { .. } |
HrmpChannelAccepted { .. } |
HrmpChannelClosing { .. } => Ok(()),
_ => Err(ProcessMessageError::BadFormat),
})?;
Ok(())
}
}
pub struct DenyThenTry<Deny, Allow>(PhantomData<Deny>, PhantomData<Allow>)
where
Deny: ShouldExecute,
Allow: ShouldExecute;
impl<Deny, Allow> ShouldExecute for DenyThenTry<Deny, Allow>
where
Deny: ShouldExecute,
Allow: ShouldExecute,
{
fn should_execute<RuntimeCall>(
origin: &Location,
message: &mut [Instruction<RuntimeCall>],
max_weight: Weight,
properties: &mut Properties,
) -> Result<(), ProcessMessageError> {
Deny::should_execute(origin, message, max_weight, properties)?;
Allow::should_execute(origin, message, max_weight, properties)
}
}
pub struct DenyReserveTransferToRelayChain;
impl ShouldExecute for DenyReserveTransferToRelayChain {
fn should_execute<RuntimeCall>(
origin: &Location,
message: &mut [Instruction<RuntimeCall>],
_max_weight: Weight,
_properties: &mut Properties,
) -> Result<(), ProcessMessageError> {
message.matcher().match_next_inst_while(
|_| true,
|inst| match inst {
InitiateReserveWithdraw {
reserve: Location { parents: 1, interior: Here },
..
} |
DepositReserveAsset { dest: Location { parents: 1, interior: Here }, .. } |
TransferReserveAsset { dest: Location { parents: 1, interior: Here }, .. } => {
Err(ProcessMessageError::Unsupported) },
ReserveAssetDeposited { .. }
if matches!(origin, Location { parents: 1, interior: Here }) =>
{
log::warn!(
target: "xcm::barrier",
"Unexpected ReserveAssetDeposited from the Relay Chain",
);
Ok(ControlFlow::Continue(()))
},
_ => Ok(ControlFlow::Continue(())),
},
)?;
Ok(())
}
}