Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
5 changes: 4 additions & 1 deletion polkadot/xcm/xcm-builder/src/tests/origins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,10 @@ fn unpaid_execution_should_work() {
Weight::from_parts(50, 50),
Weight::zero(),
);
assert_eq!(r, Outcome::Error { error: XcmError::Barrier });
assert_eq!(
r,
Outcome::Incomplete { used: Weight::from_parts(10, 10), error: XcmError::Barrier }
);

let message = Xcm(vec![UnpaidExecution {
weight_limit: Limited(Weight::from_parts(10, 10)),
Expand Down
5 changes: 4 additions & 1 deletion polkadot/xcm/xcm-builder/src/tests/querying.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,5 +130,8 @@ fn prepaid_result_of_query_should_get_free_execution() {
weight_limit,
Weight::zero(),
);
assert_eq!(r, Outcome::Error { error: XcmError::Barrier });
assert_eq!(
r,
Outcome::Incomplete { used: Weight::from_parts(10, 10), error: XcmError::Barrier }
);
}
19 changes: 14 additions & 5 deletions polkadot/xcm/xcm-builder/src/tests/version_subscriptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn simple_version_subscriptions_should_work() {
weight_limit,
Weight::zero(),
),
Outcome::Error { error: XcmError::Barrier }
Outcome::Incomplete { used: Weight::from_parts(20, 20), error: XcmError::Barrier }
);

// this case fails because the additional `SetAppendix` instruction is not allowed in the
Expand All @@ -50,7 +50,7 @@ fn simple_version_subscriptions_should_work() {
weight_limit,
Weight::zero(),
),
Outcome::Error { error: XcmError::Barrier }
Outcome::Incomplete { used: Weight::from_parts(20, 20), error: XcmError::Barrier }
);

let message = Xcm::<TestCall>(vec![SubscribeVersion {
Expand All @@ -66,7 +66,10 @@ fn simple_version_subscriptions_should_work() {
weight_limit,
Weight::zero(),
);
assert_eq!(r, Outcome::Error { error: XcmError::Barrier });
assert_eq!(
r,
Outcome::Incomplete { used: Weight::from_parts(10, 10), error: XcmError::Barrier }
);

let r = XcmExecutor::<TestConfig>::prepare_and_execute(
Parent,
Expand Down Expand Up @@ -139,7 +142,10 @@ fn simple_version_unsubscriptions_should_work() {
weight_limit,
Weight::zero(),
);
assert_eq!(r, Outcome::Error { error: XcmError::Barrier });
assert_eq!(
r,
Outcome::Incomplete { used: Weight::from_parts(20, 20), error: XcmError::Barrier }
);

let origin = Parachain(1000);
let message = Xcm::<TestCall>(vec![UnsubscribeVersion]);
Expand All @@ -152,7 +158,10 @@ fn simple_version_unsubscriptions_should_work() {
weight_limit,
Weight::zero(),
);
assert_eq!(r, Outcome::Error { error: XcmError::Barrier });
assert_eq!(
r,
Outcome::Incomplete { used: Weight::from_parts(10, 10), error: XcmError::Barrier }
);

let r = XcmExecutor::<TestConfig>::prepare_and_execute(
Parent,
Expand Down
105 changes: 105 additions & 0 deletions polkadot/xcm/xcm-builder/tests/scenarios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,3 +322,108 @@ fn reserve_based_transfer_works() {
);
});
}

/// Scenario:
/// A recursive XCM that triggers itself via `SetAppendix`.
/// The execution should fail due to inner filter.
#[test]
fn recursive_xcm_execution_fail() {
use crate::mock::*;
use frame_support::traits::{Everything, Nothing, ProcessMessageError};
use staging_xcm_builder::*;
use std::ops::ControlFlow;
use xcm::opaque::latest::prelude::*;
use xcm_executor::traits::{DenyExecution, Properties, ShouldExecute};

// Dummy filter to allow all
struct AllowAll;
impl ShouldExecute for AllowAll {
fn should_execute<RuntimeCall>(
_: &Location,
_: &mut [Instruction<RuntimeCall>],
_: Weight,
_: &mut Properties,
) -> Result<(), ProcessMessageError> {
Ok(())
}
}

// Dummy filter which denies `ClearOrigin`
struct DenyClearOrigin;
impl DenyExecution for DenyClearOrigin {
fn deny_execution<RuntimeCall>(
_: &Location,
instructions: &mut [Instruction<RuntimeCall>],
_: Weight,
_: &mut Properties,
) -> Result<(), ProcessMessageError> {
instructions.matcher().match_next_inst_while(
|_| true,
|inst| match inst {
ClearOrigin => Err(ProcessMessageError::Unsupported),
_ => Ok(ControlFlow::Continue(())),
},
)?;
Ok(())
}
}

struct XcmTestConfig;
impl xcm_executor::Config for XcmTestConfig {
type RuntimeCall = RuntimeCall;
type XcmSender = TestXcmRouter;
type XcmEventEmitter = ();
type AssetTransactor = LocalAssetTransactor;
type OriginConverter = ();
type IsReserve = ();
type IsTeleporter = TrustedTeleporters;
type UniversalLocation = UniversalLocation;
type Barrier = DenyThenTry<DenyRecursively<DenyClearOrigin>, AllowAll>;
type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
type Trader = FixedRateOfFungible<KsmPerSecondPerByte, ()>;
type ResponseHandler = XcmPallet;
type AssetTrap = XcmPallet;
type AssetLocker = ();
type AssetExchanger = ();
type AssetClaims = XcmPallet;
type SubscriptionService = XcmPallet;
type PalletInstancesInfo = AllPalletsWithSystem;
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
type FeeManager = ();
type MessageExporter = ();
type UniversalAliases = Nothing;
type CallDispatcher = RuntimeCall;
type SafeCallFilter = Everything;
type Aliasers = Nothing;
type TransactionalProcessor = ();
type HrmpNewChannelOpenRequestHandler = ();
type HrmpChannelAcceptedHandler = ();
type HrmpChannelClosingHandler = ();
type XcmRecorder = XcmPallet;
}

let para_acc: AccountId = ParaId::from(PARA_ID).into_account_truncating();
let balances = vec![(ALICE, INITIAL_BALANCE), (para_acc.clone(), INITIAL_BALANCE)];
let origin = Parachain(PARA_ID);
let message = Xcm(vec![SetAppendix(Xcm(vec![SetAppendix(Xcm(vec![ClearOrigin]))]))]);
let mut hash = fake_message_hash(&message);
let weight = BaseXcmWeight::get() * 3;

kusama_like_with_balances(balances).execute_with(|| {
let outcome = XcmExecutor::<XcmTestConfig>::prepare_and_execute(
origin,
message,
&mut hash,
weight,
Weight::zero(),
);

assert_eq!(
outcome,
Outcome::Incomplete {
used: Weight::from_parts(3000000000, 3072),
error: XcmError::Barrier
}
);
});
}
6 changes: 5 additions & 1 deletion polkadot/xcm/xcm-executor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,11 @@ impl<Config: config::Config> ExecuteXcm<Config::RuntimeCall> for XcmExecutor<Con
error = ?e,
"Barrier blocked execution",
);
return Outcome::Error { error: XcmError::Barrier }

return Outcome::Incomplete {
used: xcm_weight, // Weight consumed before the error
error: XcmError::Barrier, // The error that occurred
};
}

*id = properties.message_id.unwrap_or(*id);
Expand Down
15 changes: 15 additions & 0 deletions prdoc/pr_7967.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
title: Fix XCM Barrier Rejection Handling to Return Incomplete with Weight
doc:
- audience: Runtime Dev
description: "This PR addresses an issue with the handling of message execution\
\ when blocked by the barrier. Instead of returning an `Outcome::Error`, we modify\
\ the behaviour to return `Outcome::Incomplete`, which includes the weight consumed\
\ up to the point of rejection and the error that caused the blockage.\n\nThis\
\ change ensures more accurate weight tracking during message execution, even\
\ when interrupted. It improves resource management and aligns the XCM executor\u2019\
s behaviour with better error handling practices."
crates:
- name: staging-xcm-executor
bump: patch
- name: staging-xcm-builder
bump: patch