Add validate_xcm_nesting to the ParentAsUmp and ChildParachainRouter (#4236)

This PR:
- moves `validate_xcm_nesting` from `XcmpQueue` into the `VersionedXcm`
- adds `validate_xcm_nesting` to the `ParentAsUmp`
- adds `validate_xcm_nesting` to the `ChildParachainRouter`


Based on discussion
[here](https://github.com/paritytech/polkadot-sdk/pull/4186#discussion_r1571344270)
and/or
[here](https://github.com/paritytech/polkadot-sdk/pull/4186#discussion_r1572076666)
and/or [here]()

## Question/TODO

- [x] To the
[comment](https://github.com/paritytech/polkadot-sdk/pull/4186#discussion_r1572072295)
- Why was `validate_xcm_nesting` added just to the `XcmpQueue` router
and nowhere else? What kind of problem `MAX_XCM_DECODE_DEPTH` is
solving? (see
[comment](https://github.com/paritytech/polkadot-sdk/pull/4236#discussion_r1574605191))
This commit is contained in:
Branislav Kontur
2024-04-23 10:38:20 +02:00
committed by GitHub
parent 157294b0d3
commit 7f1646eb38
12 changed files with 237 additions and 69 deletions
+2 -15
View File
@@ -916,7 +916,8 @@ impl<T: Config> SendXcm for Pallet<T> {
let price = T::PriceForSiblingDelivery::price_for_delivery(id, &xcm);
let versioned_xcm = T::VersionWrapper::wrap_version(&d, xcm)
.map_err(|()| SendError::DestinationUnsupported)?;
validate_xcm_nesting(&versioned_xcm)
versioned_xcm
.validate_xcm_nesting()
.map_err(|()| SendError::ExceedsMaxMessageSize)?;
Ok(((id, versioned_xcm), price))
@@ -932,10 +933,6 @@ impl<T: Config> SendXcm for Pallet<T> {
fn deliver((id, xcm): (ParaId, VersionedXcm<()>)) -> Result<XcmHash, SendError> {
let hash = xcm.using_encoded(sp_io::hashing::blake2_256);
defensive_assert!(
validate_xcm_nesting(&xcm).is_ok(),
"Tickets are valid prior to delivery by trait XCM; qed"
);
match Self::send_fragment(id, XcmpMessageFormat::ConcatenatedVersionedXcm, xcm) {
Ok(_) => {
@@ -950,16 +947,6 @@ impl<T: Config> SendXcm for Pallet<T> {
}
}
/// Checks that the XCM is decodable with `MAX_XCM_DECODE_DEPTH`.
///
/// Note that this uses the limit of the sender - not the receiver. It it best effort.
pub(crate) fn validate_xcm_nesting(xcm: &VersionedXcm<()>) -> Result<(), ()> {
xcm.using_encoded(|mut enc| {
VersionedXcm::<()>::decode_all_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut enc).map(|_| ())
})
.map_err(|_| ())
}
impl<T: Config> FeeTracker for Pallet<T> {
type Id = ParaId;
@@ -1519,27 +1519,23 @@ impl_runtime_apis! {
fn worst_case_holding(depositable_count: u32) -> xcm::v4::Assets {
// A mix of fungible, non-fungible, and concrete assets.
let holding_non_fungibles = MaxAssetsIntoHolding::get() / 2 - depositable_count;
let holding_fungibles = holding_non_fungibles.saturating_sub(1);
let holding_fungibles = holding_non_fungibles.saturating_sub(2); // -2 for two `iter::once` bellow
let fungibles_amount: u128 = 100;
let mut assets = (0..holding_fungibles)
(0..holding_fungibles)
.map(|i| {
Asset {
id: GeneralIndex(i as u128).into(),
fun: Fungible(fungibles_amount * i as u128),
fun: Fungible(fungibles_amount * (i + 1) as u128), // non-zero amount
}
})
.chain(core::iter::once(Asset { id: Here.into(), fun: Fungible(u128::MAX) }))
.chain(core::iter::once(Asset { id: AssetId(TokenLocation::get()), fun: Fungible(1_000_000 * UNITS) }))
.chain((0..holding_non_fungibles).map(|i| Asset {
id: GeneralIndex(i as u128).into(),
fun: NonFungible(asset_instance_from(i)),
}))
.collect::<Vec<_>>();
assets.push(Asset {
id: AssetId(TokenLocation::get()),
fun: Fungible(1_000_000 * UNITS),
});
assets.into()
.collect::<Vec<_>>()
.into()
}
}
@@ -1610,27 +1610,23 @@ impl_runtime_apis! {
fn worst_case_holding(depositable_count: u32) -> xcm::v4::Assets {
// A mix of fungible, non-fungible, and concrete assets.
let holding_non_fungibles = MaxAssetsIntoHolding::get() / 2 - depositable_count;
let holding_fungibles = holding_non_fungibles - 1;
let holding_fungibles = holding_non_fungibles - 2; // -2 for two `iter::once` bellow
let fungibles_amount: u128 = 100;
let mut assets = (0..holding_fungibles)
(0..holding_fungibles)
.map(|i| {
Asset {
id: AssetId(GeneralIndex(i as u128).into()),
fun: Fungible(fungibles_amount * i as u128),
fun: Fungible(fungibles_amount * (i + 1) as u128), // non-zero amount
}
})
.chain(core::iter::once(Asset { id: AssetId(Here.into()), fun: Fungible(u128::MAX) }))
.chain(core::iter::once(Asset { id: AssetId(WestendLocation::get()), fun: Fungible(1_000_000 * UNITS) }))
.chain((0..holding_non_fungibles).map(|i| Asset {
id: AssetId(GeneralIndex(i as u128).into()),
fun: NonFungible(asset_instance_from(i)),
}))
.collect::<Vec<_>>();
assets.push(Asset {
id: AssetId(WestendLocation::get()),
fun: Fungible(1_000_000 * UNITS),
});
assets.into()
.collect::<Vec<_>>()
.into()
}
}
+28
View File
@@ -69,6 +69,9 @@ where
let price = P::price_for_delivery((), &xcm);
let versioned_xcm =
W::wrap_version(&d, xcm).map_err(|()| SendError::DestinationUnsupported)?;
versioned_xcm
.validate_xcm_nesting()
.map_err(|()| SendError::ExceedsMaxMessageSize)?;
let data = versioned_xcm.encode();
Ok((data, price))
@@ -526,6 +529,8 @@ impl<
mod test_xcm_router {
use super::*;
use cumulus_primitives_core::UpwardMessage;
use frame_support::assert_ok;
use xcm::MAX_XCM_DECODE_DEPTH;
/// Validates [`validate`] for required Some(destination) and Some(message)
struct OkFixedXcmHashWithAssertingRequiredInputsSender;
@@ -621,6 +626,29 @@ mod test_xcm_router {
)>(dest.into(), message)
);
}
#[test]
fn parent_as_ump_validate_nested_xcm_works() {
let dest = Parent;
type Router = ParentAsUmp<(), (), ()>;
// Message that is not too deeply nested:
let mut good = Xcm(vec![ClearOrigin]);
for _ in 0..MAX_XCM_DECODE_DEPTH - 1 {
good = Xcm(vec![SetAppendix(good)]);
}
// Check that the good message is validated:
assert_ok!(<Router as SendXcm>::validate(&mut Some(dest.into()), &mut Some(good.clone())));
// Nesting the message one more time should reject it:
let bad = Xcm(vec![SetAppendix(good)]);
assert_eq!(
Err(SendError::ExceedsMaxMessageSize),
<Router as SendXcm>::validate(&mut Some(dest.into()), &mut Some(bad))
);
}
}
#[cfg(test)]
mod test_trader {