use crate::{CreateMatcher, MatchXcm};
use frame_support::{
ensure,
traits::{Contains, Get, ProcessMessageError},
};
use polkadot_parachain::primitives::IsSystem;
use sp_std::{cell::Cell, marker::PhantomData, ops::ControlFlow, result::Result};
use xcm::prelude::*;
use xcm_executor::traits::{CheckSuspension, OnResponse, Properties, ShouldExecute};
pub struct TakeWeightCredit;
impl ShouldExecute for TakeWeightCredit {
fn should_execute<RuntimeCall>(
_origin: &MultiLocation,
_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 = 1;
pub struct AllowTopLevelPaidExecutionFrom<T>(PhantomData<T>);
impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFrom<T> {
fn should_execute<RuntimeCall>(
origin: &MultiLocation,
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 {
ReceiveTeleportedAsset(..) | ReserveAssetDeposited(..) => Ok(()),
WithdrawAsset(ref assets) if assets.len() <= MAX_ASSETS_FOR_BUY_EXECUTION => Ok(()),
ClaimAsset { ref assets, .. } if assets.len() <= MAX_ASSETS_FOR_BUY_EXECUTION =>
Ok(()),
_ => Err(ProcessMessageError::BadFormat),
})?
.skip_inst_while(|inst| matches!(inst, ClearOrigin))?
.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(())
},
_ => Err(ProcessMessageError::Overweight(max_weight)),
})?;
Ok(())
}
}
pub struct WithComputedOrigin<InnerBarrier, LocalUniversal, MaxPrefixes>(
PhantomData<(InnerBarrier, LocalUniversal, MaxPrefixes)>,
);
impl<
InnerBarrier: ShouldExecute,
LocalUniversal: Get<InteriorMultiLocation>,
MaxPrefixes: Get<u32>,
> ShouldExecute for WithComputedOrigin<InnerBarrier, LocalUniversal, MaxPrefixes>
{
fn should_execute<Call>(
origin: &MultiLocation,
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;
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 = X1(*new_global).relative_to(&LocalUniversal::get());
},
DescendOrigin(j) => {
let Ok(_) = actual_origin.append_with(*j) 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: &MultiLocation,
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: &MultiLocation,
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<MultiLocation>> ShouldExecute for AllowUnpaidExecutionFrom<T> {
fn should_execute<RuntimeCall>(
origin: &MultiLocation,
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<MultiLocation>> ShouldExecute for AllowExplicitUnpaidExecutionFrom<T> {
fn should_execute<Call>(
origin: &MultiLocation,
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<MultiLocation> for IsChildSystemParachain<ParaId> {
fn contains(l: &MultiLocation) -> bool {
matches!(
l.interior(),
Junctions::X1(Junction::Parachain(id))
if ParaId::from(*id).is_system() && l.parent_count() == 0,
)
}
}
pub struct AllowKnownQueryResponses<ResponseHandler>(PhantomData<ResponseHandler>);
impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<ResponseHandler> {
fn should_execute<RuntimeCall>(
origin: &MultiLocation,
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<MultiLocation>> ShouldExecute for AllowSubscriptionsFrom<T> {
fn should_execute<RuntimeCall>(
origin: &MultiLocation,
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 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: &MultiLocation,
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: &MultiLocation,
message: &mut [Instruction<RuntimeCall>],
_max_weight: Weight,
_properties: &mut Properties,
) -> Result<(), ProcessMessageError> {
message.matcher().match_next_inst_while(
|_| true,
|inst| match inst {
InitiateReserveWithdraw {
reserve: MultiLocation { parents: 1, interior: Here },
..
} |
DepositReserveAsset {
dest: MultiLocation { parents: 1, interior: Here }, ..
} |
TransferReserveAsset {
dest: MultiLocation { parents: 1, interior: Here }, ..
} => {
Err(ProcessMessageError::Unsupported) },
ReserveAssetDeposited { .. }
if matches!(origin, MultiLocation { parents: 1, interior: Here }) =>
{
log::warn!(
target: "xcm::barrier",
"Unexpected ReserveAssetDeposited from the Relay Chain",
);
Ok(ControlFlow::Continue(()))
},
_ => Ok(ControlFlow::Continue(())),
},
)?;
Ok(())
}
}