Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

XCM: Implement a blocking barrier #6670

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion xcm/pallet-xcm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ use frame_system::pallet_prelude::*;
pub use pallet::*;
use xcm_executor::{
traits::{
ClaimAssets, DropAssets, MatchesFungible, OnResponse, VersionChangeNotifier, WeightBounds,
CheckSuspension, ClaimAssets, DropAssets, MatchesFungible, OnResponse,
VersionChangeNotifier, WeightBounds,
},
Assets,
};
Expand Down Expand Up @@ -610,6 +611,11 @@ pub mod pallet {
OptionQuery,
>;

/// Global suspension state of the XCM executor.
#[pallet::storage]
#[pallet::getter(fn suspended)]
pub(super) type XcmExecutionSuspended<T: Config> = StorageValue<_, bool, ValueQuery>;

#[pallet::genesis_config]
pub struct GenesisConfig {
/// The default version to encode outgoing XCM messages with.
Expand Down Expand Up @@ -1090,6 +1096,18 @@ pub mod pallet {
Some(weight_limit),
)
}

/// Set or unset the global suspension state of the XCM executor.
///
/// - `origin`: Must be Root.
/// - `suspended`: `true` to suspend, `false` to resume.
#[pallet::call_index(10)]
#[pallet::weight(100_000_000u64)]
pub fn force_suspension(origin: OriginFor<T>, suspended: bool) -> DispatchResult {
ensure_root(origin)?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably be a configuration item.

XcmExecutionSuspended::<T>::set(suspended);
Ok(())
}
}
}

Expand Down Expand Up @@ -2036,6 +2054,17 @@ impl<T: Config> OnResponse for Pallet<T> {
}
}

impl<T: Config> CheckSuspension for Pallet<T> {
fn is_suspended<Call>(
_origin: &MultiLocation,
_instructions: &mut [Instruction<Call>],
_max_weight: Weight,
_weight_credit: &mut Weight,
) -> bool {
Self::suspended()
}
}

/// Ensure that the origin `o` represents an XCM (`Transact`) origin.
///
/// Returns `Ok` with the location of the XCM sender or an `Err` otherwise.
Expand Down
96 changes: 63 additions & 33 deletions xcm/xcm-builder/src/barriers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use xcm::latest::{
MultiLocation, Weight,
WeightLimit::*,
};
use xcm_executor::traits::{OnResponse, ShouldExecute};
use xcm_executor::traits::{CheckSuspension, OnResponse, RejectReason, ShouldExecute};

/// Execution barrier that just takes `max_weight` from `weight_credit`.
///
Expand All @@ -43,13 +43,14 @@ impl ShouldExecute for TakeWeightCredit {
_instructions: &mut [Instruction<RuntimeCall>],
max_weight: Weight,
weight_credit: &mut Weight,
) -> Result<(), ()> {
) -> Result<(), RejectReason> {
log::trace!(
target: "xcm::barriers",
"TakeWeightCredit origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
_origin, _instructions, max_weight, weight_credit,
);
*weight_credit = weight_credit.checked_sub(&max_weight).ok_or(())?;
*weight_credit =
weight_credit.checked_sub(&max_weight).ok_or(RejectReason::InsufficientCredit)?;
Ok(())
}
}
Expand All @@ -66,42 +67,43 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFro
instructions: &mut [Instruction<RuntimeCall>],
max_weight: Weight,
_weight_credit: &mut Weight,
) -> Result<(), ()> {
) -> Result<(), RejectReason> {
log::trace!(
target: "xcm::barriers",
"AllowTopLevelPaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, instructions, max_weight, _weight_credit,
);

ensure!(T::contains(origin), ());
ensure!(T::contains(origin), RejectReason::UntrustedOrigin);
// We will read up to 5 instructions. This allows up to 3 `ClearOrigin` instructions. We
// allow for more than one since anything beyond the first is a no-op and it's conceivable
// that composition of operations might result in more than one being appended.
let mut iter = instructions.iter_mut().take(5);
let i = iter.next().ok_or(())?;
let i = iter.next().ok_or(RejectReason::UnexpectedMessageFormat)?;
match i {
ReceiveTeleportedAsset(..) |
WithdrawAsset(..) |
ReserveAssetDeposited(..) |
ClaimAsset { .. } => (),
_ => return Err(()),
_ => return Err(RejectReason::UnexpectedMessageFormat),
}
let mut i = iter.next().ok_or(())?;
let mut i = iter.next().ok_or(RejectReason::UnexpectedMessageFormat)?;
while let ClearOrigin = i {
i = iter.next().ok_or(())?;
i = iter.next().ok_or(RejectReason::UnexpectedMessageFormat)?;
}
match i {
BuyExecution { weight_limit: Limited(ref mut weight), .. }
if weight.all_gte(max_weight) =>
{
*weight = weight.max(max_weight);
Ok(())
},
BuyExecution { weight_limit: Limited(ref mut weight), .. } =>
if weight.all_gte(max_weight) {
*weight = weight.max(max_weight);
Ok(())
} else {
Err(RejectReason::WeightLimitTooLow)
},
BuyExecution { ref mut weight_limit, .. } if weight_limit == &Unlimited => {
*weight_limit = Limited(max_weight);
Ok(())
},
_ => Err(()),
_ => Err(RejectReason::UnexpectedMessageFormat),
}
}
}
Expand Down Expand Up @@ -165,7 +167,7 @@ impl<
instructions: &mut [Instruction<Call>],
max_weight: Weight,
weight_credit: &mut Weight,
) -> Result<(), ()> {
) -> Result<(), RejectReason> {
log::trace!(
target: "xcm::barriers",
"WithComputedOrigin origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
Expand All @@ -175,7 +177,7 @@ impl<
let mut skipped = 0;
// NOTE: We do not check the validity of `UniversalOrigin` here, meaning that a malicious
// origin could place a `UniversalOrigin` in order to spoof some location which gets free
// execution. This technical could get it past the barrier condition, but the execution
// execution. This technically could get it past the barrier condition, but the execution
// would instantly fail since the first instruction would cause an error with the
// invalid UniversalOrigin.
while skipped < MaxPrefixes::get() as usize {
Expand All @@ -186,7 +188,9 @@ impl<
actual_origin = X1(*new_global).relative_to(&LocalUniversal::get());
},
Some(DescendOrigin(j)) => {
actual_origin.append_with(*j).map_err(|_| ())?;
actual_origin
.append_with(*j)
.map_err(|_| RejectReason::OriginMultiLocationTooLong)?;
},
_ => break,
}
Expand All @@ -201,6 +205,28 @@ impl<
}
}

/// Barrier condition that allows for a `SuspensionChecker` that controls whether or not the XCM
/// executor will be suspended from executing the given XCM.
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,
weight_credit: &mut Weight,
) -> Result<(), RejectReason> {
if SuspensionChecker::is_suspended(origin, instructions, max_weight, weight_credit) {
Err(RejectReason::Suspended)
} else {
Inner::should_execute(origin, instructions, max_weight, weight_credit)
}
}
}

/// Allows execution from any origin that is contained in `T` (i.e. `T::Contains(origin)`).
///
/// Use only for executions from completely trusted origins, from which no unpermissioned messages
Expand All @@ -212,13 +238,13 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowUnpaidExecutionFrom<T> {
instructions: &mut [Instruction<RuntimeCall>],
_max_weight: Weight,
_weight_credit: &mut Weight,
) -> Result<(), ()> {
) -> Result<(), RejectReason> {
log::trace!(
target: "xcm::barriers",
"AllowUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, instructions, _max_weight, _weight_credit,
);
ensure!(T::contains(origin), ());
ensure!(T::contains(origin), RejectReason::UntrustedOrigin);
Ok(())
}
}
Expand All @@ -234,18 +260,18 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowExplicitUnpaidExecutionF
instructions: &mut [Instruction<Call>],
max_weight: Weight,
_weight_credit: &mut Weight,
) -> Result<(), ()> {
) -> Result<(), RejectReason> {
log::trace!(
target: "xcm::barriers",
"AllowExplicitUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, instructions, max_weight, _weight_credit,
);
ensure!(T::contains(origin), ());
ensure!(T::contains(origin), RejectReason::UntrustedOrigin);
match instructions.first() {
Some(UnpaidExecution { weight_limit: Limited(m), .. }) if m.all_gte(max_weight) =>
Ok(()),
Some(UnpaidExecution { weight_limit: Unlimited, .. }) => Ok(()),
_ => Err(()),
_ => Err(RejectReason::UnexpectedMessageFormat),
}
}
}
Expand All @@ -270,18 +296,22 @@ impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<Res
instructions: &mut [Instruction<RuntimeCall>],
_max_weight: Weight,
_weight_credit: &mut Weight,
) -> Result<(), ()> {
) -> Result<(), RejectReason> {
log::trace!(
target: "xcm::barriers",
"AllowKnownQueryResponses origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, instructions, _max_weight, _weight_credit,
);
ensure!(instructions.len() == 1, ());
ensure!(instructions.len() == 1, RejectReason::UnexpectedMessageFormat);
match instructions.first() {
Some(QueryResponse { query_id, querier, .. })
if ResponseHandler::expecting_response(origin, *query_id, querier.as_ref()) =>
Ok(()),
_ => Err(()),
Some(QueryResponse { query_id, querier, .. }) => {
if ResponseHandler::expecting_response(origin, *query_id, querier.as_ref()) {
Ok(())
} else {
Err(RejectReason::UnexpectedResponse)
}
},
_ => Err(RejectReason::UnexpectedMessageFormat),
}
}
}
Expand All @@ -295,16 +325,16 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowSubscriptionsFrom<T> {
instructions: &mut [Instruction<RuntimeCall>],
_max_weight: Weight,
_weight_credit: &mut Weight,
) -> Result<(), ()> {
) -> Result<(), RejectReason> {
log::trace!(
target: "xcm::barriers",
"AllowSubscriptionsFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, instructions, _max_weight, _weight_credit,
);
ensure!(T::contains(origin), ());
ensure!(T::contains(origin), RejectReason::UntrustedOrigin);
match instructions {
&mut [SubscribeVersion { .. } | UnsubscribeVersion] => Ok(()),
_ => Err(()),
_ => Err(RejectReason::UnexpectedMessageFormat),
}
}
}
2 changes: 1 addition & 1 deletion xcm/xcm-builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ mod barriers;
pub use barriers::{
AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom,
AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, IsChildSystemParachain,
TakeWeightCredit, WithComputedOrigin,
RespectSuspension, TakeWeightCredit, WithComputedOrigin,
};

mod currency_adapter;
Expand Down
Loading