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

Commit 7290787

Browse files
authored
Avoid consuming XCM message for NotApplicable scenario (#1787)
* Avoid consuming message for NotApplicable scenario * Avoid consuming message for NotApplicable scenario tests
1 parent c446b63 commit 7290787

File tree

3 files changed

+192
-4
lines changed

3 files changed

+192
-4
lines changed

pallets/xcmp-queue/src/lib.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1138,19 +1138,20 @@ impl<T: Config> SendXcm for Pallet<T> {
11381138
msg: &mut Option<Xcm<()>>,
11391139
) -> SendResult<(ParaId, VersionedXcm<()>)> {
11401140
let d = dest.take().ok_or(SendError::MissingArgument)?;
1141-
let xcm = msg.take().ok_or(SendError::MissingArgument)?;
11421141

11431142
match &d {
11441143
// An HRMP message for a sibling parachain.
11451144
MultiLocation { parents: 1, interior: X1(Parachain(id)) } => {
1145+
let xcm = msg.take().ok_or(SendError::MissingArgument)?;
11461146
let id = ParaId::from(*id);
11471147
let price = T::PriceForSiblingDelivery::price_for_sibling_delivery(id, &xcm);
11481148
let versioned_xcm = T::VersionWrapper::wrap_version(&d, xcm)
11491149
.map_err(|()| SendError::DestinationUnsupported)?;
11501150
Ok(((id, versioned_xcm), price))
11511151
},
1152-
// Anything else is unhandled. This includes a message this is meant for us.
11531152
_ => {
1153+
// Anything else is unhandled. This includes a message that is not meant for us.
1154+
// We need to make sure that dest/msg is not consumed here.
11541155
*dest = Some(d);
11551156
Err(SendError::NotApplicable)
11561157
},

pallets/xcmp-queue/src/tests.rs

+85
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,88 @@ fn update_xcmp_max_individual_weight() {
247247
assert_eq!(data.xcmp_max_individual_weight, 30u64 * WEIGHT_PER_MILLIS);
248248
});
249249
}
250+
251+
/// Validates [`validate`] for required Some(destination) and Some(message)
252+
struct OkFixedXcmHashWithAssertingRequiredInputsSender;
253+
impl OkFixedXcmHashWithAssertingRequiredInputsSender {
254+
const FIXED_XCM_HASH: [u8; 32] = [9; 32];
255+
256+
fn fixed_delivery_asset() -> MultiAssets {
257+
MultiAssets::new()
258+
}
259+
260+
fn expected_delivery_result() -> Result<(XcmHash, MultiAssets), SendError> {
261+
Ok((Self::FIXED_XCM_HASH, Self::fixed_delivery_asset()))
262+
}
263+
}
264+
impl SendXcm for OkFixedXcmHashWithAssertingRequiredInputsSender {
265+
type Ticket = ();
266+
267+
fn validate(
268+
destination: &mut Option<MultiLocation>,
269+
message: &mut Option<Xcm<()>>,
270+
) -> SendResult<Self::Ticket> {
271+
assert!(destination.is_some());
272+
assert!(message.is_some());
273+
Ok(((), OkFixedXcmHashWithAssertingRequiredInputsSender::fixed_delivery_asset()))
274+
}
275+
276+
fn deliver(_: Self::Ticket) -> Result<XcmHash, SendError> {
277+
Ok(Self::FIXED_XCM_HASH)
278+
}
279+
}
280+
281+
#[test]
282+
fn xcmp_queue_does_not_consume_dest_or_msg_on_not_applicable() {
283+
// dummy message
284+
let message = Xcm(vec![Trap(5)]);
285+
286+
// XcmpQueue - check dest is really not applicable
287+
let dest = (Parent, Parent, Parent);
288+
let mut dest_wrapper = Some(dest.clone().into());
289+
let mut msg_wrapper = Some(message.clone());
290+
assert_eq!(
291+
Err(SendError::NotApplicable),
292+
<XcmpQueue as SendXcm>::validate(&mut dest_wrapper, &mut msg_wrapper)
293+
);
294+
295+
// check wrapper were not consumed
296+
assert_eq!(Some(dest.clone().into()), dest_wrapper.take());
297+
assert_eq!(Some(message.clone()), msg_wrapper.take());
298+
299+
// another try with router chain with asserting sender
300+
assert_eq!(
301+
OkFixedXcmHashWithAssertingRequiredInputsSender::expected_delivery_result(),
302+
send_xcm::<(XcmpQueue, OkFixedXcmHashWithAssertingRequiredInputsSender)>(
303+
dest.into(),
304+
message
305+
)
306+
);
307+
}
308+
309+
#[test]
310+
fn xcmp_queue_consumes_dest_and_msg_on_ok_validate() {
311+
// dummy message
312+
let message = Xcm(vec![Trap(5)]);
313+
314+
// XcmpQueue - check dest/msg is valid
315+
let dest = (Parent, X1(Parachain(5555)));
316+
let mut dest_wrapper = Some(dest.clone().into());
317+
let mut msg_wrapper = Some(message.clone());
318+
assert!(<XcmpQueue as SendXcm>::validate(&mut dest_wrapper, &mut msg_wrapper).is_ok());
319+
320+
// check wrapper were consumed
321+
assert_eq!(None, dest_wrapper.take());
322+
assert_eq!(None, msg_wrapper.take());
323+
324+
new_test_ext().execute_with(|| {
325+
// another try with router chain with asserting sender
326+
assert_eq!(
327+
Err(SendError::Transport("NoChannel")),
328+
send_xcm::<(XcmpQueue, OkFixedXcmHashWithAssertingRequiredInputsSender)>(
329+
dest.into(),
330+
message
331+
)
332+
);
333+
});
334+
}

primitives/utility/src/lib.rs

+104-2
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,20 @@ where
7575
msg: &mut Option<Xcm<()>>,
7676
) -> SendResult<Vec<u8>> {
7777
let d = dest.take().ok_or(SendError::MissingArgument)?;
78-
let xcm = msg.take().ok_or(SendError::MissingArgument)?;
7978

8079
if d.contains_parents_only(1) {
8180
// An upward message for the relay chain.
81+
let xcm = msg.take().ok_or(SendError::MissingArgument)?;
8282
let price = P::price_for_parent_delivery(&xcm);
8383
let versioned_xcm =
8484
W::wrap_version(&d, xcm).map_err(|()| SendError::DestinationUnsupported)?;
8585
let data = versioned_xcm.encode();
8686

8787
Ok((data, price))
8888
} else {
89+
// Anything else is unhandled. This includes a message that is not meant for us.
90+
// We need to make sure that dest/msg is not consumed here.
8991
*dest = Some(d);
90-
// Anything else is unhandled. This includes a message this is meant for us.
9192
Err(SendError::NotApplicable)
9293
}
9394
}
@@ -317,3 +318,104 @@ pub trait ChargeWeightInFungibles<AccountId, Assets: fungibles::Inspect<AccountI
317318
weight: Weight,
318319
) -> Result<<Assets as Inspect<AccountId>>::Balance, XcmError>;
319320
}
321+
322+
#[cfg(test)]
323+
mod tests {
324+
use super::*;
325+
use cumulus_primitives_core::UpwardMessage;
326+
327+
/// Validates [`validate`] for required Some(destination) and Some(message)
328+
struct OkFixedXcmHashWithAssertingRequiredInputsSender;
329+
impl OkFixedXcmHashWithAssertingRequiredInputsSender {
330+
const FIXED_XCM_HASH: [u8; 32] = [9; 32];
331+
332+
fn fixed_delivery_asset() -> MultiAssets {
333+
MultiAssets::new()
334+
}
335+
336+
fn expected_delivery_result() -> Result<(XcmHash, MultiAssets), SendError> {
337+
Ok((Self::FIXED_XCM_HASH, Self::fixed_delivery_asset()))
338+
}
339+
}
340+
impl SendXcm for OkFixedXcmHashWithAssertingRequiredInputsSender {
341+
type Ticket = ();
342+
343+
fn validate(
344+
destination: &mut Option<MultiLocation>,
345+
message: &mut Option<Xcm<()>>,
346+
) -> SendResult<Self::Ticket> {
347+
assert!(destination.is_some());
348+
assert!(message.is_some());
349+
Ok(((), OkFixedXcmHashWithAssertingRequiredInputsSender::fixed_delivery_asset()))
350+
}
351+
352+
fn deliver(_: Self::Ticket) -> Result<XcmHash, SendError> {
353+
Ok(Self::FIXED_XCM_HASH)
354+
}
355+
}
356+
357+
/// Impl [`UpwardMessageSender`] that return `Other` error
358+
struct OtherErrorUpwardMessageSender;
359+
impl UpwardMessageSender for OtherErrorUpwardMessageSender {
360+
fn send_upward_message(_: UpwardMessage) -> Result<u32, MessageSendError> {
361+
Err(MessageSendError::Other)
362+
}
363+
}
364+
365+
#[test]
366+
fn parent_as_ump_does_not_consume_dest_or_msg_on_not_applicable() {
367+
// dummy message
368+
let message = Xcm(vec![Trap(5)]);
369+
370+
// ParentAsUmp - check dest is really not applicable
371+
let dest = (Parent, Parent, Parent);
372+
let mut dest_wrapper = Some(dest.clone().into());
373+
let mut msg_wrapper = Some(message.clone());
374+
assert_eq!(
375+
Err(SendError::NotApplicable),
376+
<ParentAsUmp<(), (), ()> as SendXcm>::validate(&mut dest_wrapper, &mut msg_wrapper)
377+
);
378+
379+
// check wrapper were not consumed
380+
assert_eq!(Some(dest.clone().into()), dest_wrapper.take());
381+
assert_eq!(Some(message.clone()), msg_wrapper.take());
382+
383+
// another try with router chain with asserting sender
384+
assert_eq!(
385+
OkFixedXcmHashWithAssertingRequiredInputsSender::expected_delivery_result(),
386+
send_xcm::<(ParentAsUmp<(), (), ()>, OkFixedXcmHashWithAssertingRequiredInputsSender)>(
387+
dest.into(),
388+
message
389+
)
390+
);
391+
}
392+
393+
#[test]
394+
fn parent_as_ump_consumes_dest_and_msg_on_ok_validate() {
395+
// dummy message
396+
let message = Xcm(vec![Trap(5)]);
397+
398+
// ParentAsUmp - check dest/msg is valid
399+
let dest = (Parent, Here);
400+
let mut dest_wrapper = Some(dest.clone().into());
401+
let mut msg_wrapper = Some(message.clone());
402+
assert!(<ParentAsUmp<(), (), ()> as SendXcm>::validate(
403+
&mut dest_wrapper,
404+
&mut msg_wrapper
405+
)
406+
.is_ok());
407+
408+
// check wrapper were consumed
409+
assert_eq!(None, dest_wrapper.take());
410+
assert_eq!(None, msg_wrapper.take());
411+
412+
// another try with router chain with asserting sender
413+
assert_eq!(
414+
Err(SendError::Transport("Other")),
415+
send_xcm::<(
416+
ParentAsUmp<OtherErrorUpwardMessageSender, (), ()>,
417+
OkFixedXcmHashWithAssertingRequiredInputsSender
418+
)>(dest.into(), message)
419+
);
420+
}
421+
}

0 commit comments

Comments
 (0)