Skip to content

Commit

Permalink
Added AllowHrmpNotificationsFromRelayChain XCM barrier
Browse files Browse the repository at this point in the history
  • Loading branch information
bkontur committed Apr 22, 2024
1 parent 253778c commit 71834c6
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 54 deletions.
35 changes: 35 additions & 0 deletions polkadot/xcm/xcm-builder/src/barriers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,41 @@ impl<T: Contains<Location>> ShouldExecute for AllowSubscriptionsFrom<T> {
}
}

/// Allows execution for the Relay Chain origin (represented as `Location::parent()`) if it is just
/// a straight `HrmpNewChannelOpenRequest`, `HrmpChannelAccepted`, or `HrmpChannelClosing`
/// instruction.
///
/// Note: This barrier fulfills safety recommendations for the mentioned instructions - see their
/// documentation.
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,
);
// accept only the Relay Chain
ensure!(matches!(origin.unpack(), (1, [])), ProcessMessageError::Unsupported);
// accept only HRMP notifications and nothing else
instructions
.matcher()
.assert_remaining_insts(1)?
.match_next_inst(|inst| match inst {
HrmpNewChannelOpenRequest { .. } |
HrmpChannelAccepted { .. } |
HrmpChannelClosing { .. } => Ok(()),
_ => Err(ProcessMessageError::BadFormat),
})?;
Ok(())
}
}

/// Deny executing the XCM if it matches any of the Deny filter regardless of anything else.
/// If it passes the Deny, and matches one of the Allow cases then it is let through.
pub struct DenyThenTry<Deny, Allow>(PhantomData<Deny>, PhantomData<Allow>)
Expand Down
9 changes: 5 additions & 4 deletions polkadot/xcm/xcm-builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ pub use asset_conversion::{

mod barriers;
pub use barriers::{
AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom,
AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, DenyReserveTransferToRelayChain,
DenyThenTry, IsChildSystemParachain, IsParentsOnly, IsSiblingSystemParachain,
RespectSuspension, TakeWeightCredit, TrailingSetTopicAsId, WithComputedOrigin,
AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain,
AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom,
AllowUnpaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, IsChildSystemParachain,
IsParentsOnly, IsSiblingSystemParachain, RespectSuspension, TakeWeightCredit,
TrailingSetTopicAsId, WithComputedOrigin,
};

mod controller;
Expand Down
194 changes: 144 additions & 50 deletions polkadot/xcm/xcm-builder/src/tests/barriers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,56 +315,150 @@ fn allow_subscriptions_from_should_work() {
// allow only parent
AllowSubsFrom::set(vec![Location::parent()]);

let valid_xcm_1 = Xcm::<TestCall>(vec![SubscribeVersion {
query_id: 42,
max_response_weight: Weight::from_parts(5000, 5000),
}]);
let valid_xcm_2 = Xcm::<TestCall>(vec![UnsubscribeVersion]);
let invalid_xcm_1 = Xcm::<TestCall>(vec![
SetAppendix(Xcm(vec![])),
SubscribeVersion { query_id: 42, max_response_weight: Weight::from_parts(5000, 5000) },
]);
let invalid_xcm_2 = Xcm::<TestCall>(vec![
SubscribeVersion { query_id: 42, max_response_weight: Weight::from_parts(5000, 5000) },
SetTopic([0; 32]),
]);
// closure for (xcm, origin) testing with `AllowSubscriptionsFrom`
let assert_should_execute = |mut xcm: Vec<Instruction<()>>, origin, expected_result| {
assert_eq!(
AllowSubscriptionsFrom::<IsInVec<AllowSubsFrom>>::should_execute(
&origin,
&mut xcm,
Weight::from_parts(10, 10),
&mut props(Weight::zero()),
),
expected_result
);
};

// invalid origin
assert_should_execute(
vec![SubscribeVersion {
query_id: Default::default(),
max_response_weight: Default::default(),
}],
Parachain(1).into_location(),
Err(ProcessMessageError::Unsupported),
);
assert_should_execute(
vec![UnsubscribeVersion],
Parachain(1).into_location(),
Err(ProcessMessageError::Unsupported),
);

let test_data = vec![
(
valid_xcm_1.clone(),
Parachain(1).into_location(),
// not allowed origin
Err(ProcessMessageError::Unsupported),
),
(valid_xcm_1, Location::parent(), Ok(())),
(
valid_xcm_2.clone(),
Parachain(1).into_location(),
// not allowed origin
Err(ProcessMessageError::Unsupported),
),
(valid_xcm_2, Location::parent(), Ok(())),
(
invalid_xcm_1,
Location::parent(),
// invalid XCM
Err(ProcessMessageError::BadFormat),
),
(
invalid_xcm_2,
Location::parent(),
// invalid XCM
Err(ProcessMessageError::BadFormat),
),
];

for (mut message, origin, expected_result) in test_data {
let r = AllowSubscriptionsFrom::<IsInVec<AllowSubsFrom>>::should_execute(
&origin,
message.inner_mut(),
Weight::from_parts(10, 10),
&mut props(Weight::zero()),
// invalid XCM (unexpected instruction before)
assert_should_execute(
vec![
SetAppendix(Xcm(vec![])),
SubscribeVersion {
query_id: Default::default(),
max_response_weight: Default::default(),
},
],
Location::parent(),
Err(ProcessMessageError::BadFormat),
);
assert_should_execute(
vec![SetAppendix(Xcm(vec![])), UnsubscribeVersion],
Location::parent(),
Err(ProcessMessageError::BadFormat),
);
// invalid XCM (unexpected instruction after)
assert_should_execute(
vec![
SubscribeVersion {
query_id: Default::default(),
max_response_weight: Default::default(),
},
SetTopic([0; 32]),
],
Location::parent(),
Err(ProcessMessageError::BadFormat),
);
assert_should_execute(
vec![UnsubscribeVersion, SetTopic([0; 32])],
Location::parent(),
Err(ProcessMessageError::BadFormat),
);
// invalid XCM (unexpected instruction)
assert_should_execute(
vec![SetAppendix(Xcm(vec![]))],
Location::parent(),
Err(ProcessMessageError::BadFormat),
);

// ok
assert_should_execute(
vec![SubscribeVersion {
query_id: Default::default(),
max_response_weight: Default::default(),
}],
Location::parent(),
Ok(()),
);
assert_should_execute(vec![UnsubscribeVersion], Location::parent(), Ok(()));
}

#[test]
fn allow_hrmp_notifications_from_relay_chain_should_work() {
// closure for (xcm, origin) testing with `AllowHrmpNotificationsFromRelayChain`
let assert_should_execute = |mut xcm: Vec<Instruction<()>>, origin, expected_result| {
assert_eq!(
AllowHrmpNotificationsFromRelayChain::should_execute(
&origin,
&mut xcm,
Weight::from_parts(10, 10),
&mut props(Weight::zero()),
),
expected_result
);
assert_eq!(r, expected_result, "Failed for origin: {origin:?} and message: {message:?}");
}
};

// invalid origin
assert_should_execute(
vec![HrmpChannelAccepted { recipient: Default::default() }],
Location::new(1, [Parachain(1)]),
Err(ProcessMessageError::Unsupported),
);

// invalid XCM (unexpected instruction before)
assert_should_execute(
vec![SetAppendix(Xcm(vec![])), HrmpChannelAccepted { recipient: Default::default() }],
Location::parent(),
Err(ProcessMessageError::BadFormat),
);
// invalid XCM (unexpected instruction after)
assert_should_execute(
vec![HrmpChannelAccepted { recipient: Default::default() }, SetTopic([0; 32])],
Location::parent(),
Err(ProcessMessageError::BadFormat),
);
// invalid XCM (unexpected instruction)
assert_should_execute(
vec![SetAppendix(Xcm(vec![]))],
Location::parent(),
Err(ProcessMessageError::BadFormat),
);

// ok
assert_should_execute(
vec![HrmpChannelAccepted { recipient: Default::default() }],
Location::parent(),
Ok(()),
);
assert_should_execute(
vec![HrmpNewChannelOpenRequest {
max_capacity: Default::default(),
sender: Default::default(),
max_message_size: Default::default(),
}],
Location::parent(),
Ok(()),
);
assert_should_execute(
vec![HrmpChannelClosing {
recipient: Default::default(),
sender: Default::default(),
initiator: Default::default(),
}],
Location::parent(),
Ok(()),
);
}

0 comments on commit 71834c6

Please sign in to comment.