From 9331a6cc58650d8a3b68b5c4d520de0e463b2a50 Mon Sep 17 00:00:00 2001
From: Branislav Kontur <bkontur@gmail.com>
Date: Thu, 18 Apr 2024 10:59:38 +0200
Subject: [PATCH 1/6] Add `EnsureDecodableXcm` router (for testing purposes)

---
 polkadot/xcm/xcm-builder/src/lib.rs     |  2 +-
 polkadot/xcm/xcm-builder/src/routing.rs | 66 +++++++++++++++++++++++++
 2 files changed, 67 insertions(+), 1 deletion(-)

diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs
index bd4a4c941c915..cdc663a0cc9b4 100644
--- a/polkadot/xcm/xcm-builder/src/lib.rs
+++ b/polkadot/xcm/xcm-builder/src/lib.rs
@@ -119,7 +119,7 @@ mod process_xcm_message;
 pub use process_xcm_message::ProcessXcmMessage;
 
 mod routing;
-pub use routing::{EnsureDelivery, WithTopicSource, WithUniqueTopic};
+pub use routing::{EnsureDecodableXcm, EnsureDelivery, WithTopicSource, WithUniqueTopic};
 
 mod transactional;
 pub use transactional::FrameTransactionalProcessor;
diff --git a/polkadot/xcm/xcm-builder/src/routing.rs b/polkadot/xcm/xcm-builder/src/routing.rs
index 529ef80c15ff1..4b4dee9f1312f 100644
--- a/polkadot/xcm/xcm-builder/src/routing.rs
+++ b/polkadot/xcm/xcm-builder/src/routing.rs
@@ -139,3 +139,69 @@ impl EnsureDelivery for Tuple {
 		(None, None)
 	}
 }
+
+/// A wrapper router that attempts to *encode* and *decode* passed XCM `message` to ensure that the
+/// receiving side will be able to decode, at least with the same XCM version.
+///
+/// This is designed to be at the top-level of any routers which do the real delivery. While other
+/// routers can manipulate the `message`, we cannot access the final XCM due to the generic
+/// `Inner::Ticket`. Therefore, this router aims to validate at least the passed `message`.
+///
+/// NOTE: For testing purposes.
+pub struct EnsureDecodableXcm<Inner>(sp_std::marker::PhantomData<Inner>);
+impl<Inner: SendXcm> SendXcm for EnsureDecodableXcm<Inner> {
+	type Ticket = Inner::Ticket;
+
+	fn validate(
+		destination: &mut Option<Location>,
+		message: &mut Option<Xcm<()>>,
+	) -> SendResult<Self::Ticket> {
+		if let Some(msg) = message {
+			if let Some(e) = Self::ensure_encode_decode(msg) {
+				return Err(e)
+			}
+		}
+		Inner::validate(destination, message)
+	}
+
+	fn deliver(ticket: Self::Ticket) -> Result<XcmHash, SendError> {
+		Inner::deliver(ticket)
+	}
+}
+impl<Inner> EnsureDecodableXcm<Inner> {
+	fn ensure_encode_decode(xcm: &Xcm<()>) -> Option<SendError> {
+		use parity_scale_codec::Decode;
+		let encoded = xcm.encode();
+		match Xcm::<()>::decode(&mut &encoded[..]) {
+			Ok(decoded_xcm) => match decoded_xcm.eq(xcm) {
+				true => None,
+				false => {
+					log::error!(target: "xcm::test_utils", "EnsureDecodableXcm `decoded_xcm` does not match `xcm` - \ndecoded_xcm: {decoded_xcm:?} \nxcm:{xcm:?}");
+					Some(SendError::Transport("Decoded xcm does not match xcm!"))
+				},
+			},
+			Err(e) => {
+				log::error!(target: "xcm::test_utils", "EnsureDecodableXcm decode error: {e:?} occurred for xcm: {xcm:?}");
+				Some(SendError::Transport("Failed to encode/decode xcm!"))
+			},
+		}
+	}
+}
+
+#[test]
+fn ensure_encode_decode_works() {
+	let good_fun = vec![(Here, 10).into(), (Parent, 10).into()];
+	assert!(Assets::from_sorted_and_deduplicated(good_fun.clone()).is_ok());
+
+	let good_xcm = Xcm(vec![ReserveAssetDeposited(Assets::from(good_fun.clone()))]);
+	assert!(EnsureDecodableXcm::<()>::ensure_encode_decode(&good_xcm).is_none());
+
+	let bad_xcm = Xcm(vec![
+		ReserveAssetDeposited(Assets::from(good_fun));
+		(xcm::latest::MAX_INSTRUCTIONS_TO_DECODE + 1) as usize
+	]);
+	assert_eq!(
+		EnsureDecodableXcm::<()>::ensure_encode_decode(&bad_xcm),
+		Some(SendError::Transport("Failed to encode/decode xcm!"))
+	);
+}

From 5f53e19c22a0324a02b4ed76da74b3f743d60a0e Mon Sep 17 00:00:00 2001
From: Branislav Kontur <bkontur@gmail.com>
Date: Thu, 18 Apr 2024 11:15:26 +0200
Subject: [PATCH 2/6] Use `EnsureDecodableXcm` for `polkadot-test-runtime`

---
 Cargo.lock                                           |  8 --------
 polkadot/runtime/test-runtime/Cargo.toml             | 10 ----------
 polkadot/runtime/test-runtime/constants/Cargo.toml   |  6 ------
 polkadot/runtime/test-runtime/src/xcm_config.rs      | 10 ++++++----
 .../xcm/pallet-xcm-benchmarks/src/fungible/mock.rs   |  6 ++++--
 .../xcm/pallet-xcm-benchmarks/src/generic/mock.rs    |  5 +++--
 polkadot/xcm/pallet-xcm/src/mock.rs                  | 12 +++++++-----
 polkadot/xcm/xcm-builder/src/tests/mock.rs           |  7 +++++--
 polkadot/xcm/xcm-builder/tests/mock/mod.rs           | 12 +++++++-----
 polkadot/xcm/xcm-simulator/example/src/parachain.rs  | 10 +++++-----
 .../xcm/xcm-simulator/example/src/relay_chain.rs     |  2 +-
 11 files changed, 38 insertions(+), 50 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 951f2548d34da..910def85c60d4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -14348,7 +14348,6 @@ dependencies = [
 name = "polkadot-test-runtime"
 version = "1.0.0"
 dependencies = [
- "bitvec",
  "frame-election-provider-support",
  "frame-executive",
  "frame-support",
@@ -14373,16 +14372,12 @@ dependencies = [
  "pallet-vesting",
  "pallet-xcm",
  "parity-scale-codec",
- "polkadot-parachain-primitives",
  "polkadot-primitives",
  "polkadot-runtime-common",
  "polkadot-runtime-parachains",
- "rustc-hex",
  "scale-info",
  "serde",
- "serde_derive",
  "serde_json",
- "smallvec",
  "sp-api",
  "sp-authority-discovery",
  "sp-block-builder",
@@ -21273,11 +21268,8 @@ version = "1.0.0"
 dependencies = [
  "frame-support",
  "polkadot-primitives",
- "polkadot-runtime-common",
  "smallvec",
- "sp-core",
  "sp-runtime",
- "sp-weights",
 ]
 
 [[package]]
diff --git a/polkadot/runtime/test-runtime/Cargo.toml b/polkadot/runtime/test-runtime/Cargo.toml
index 35fb684597e7d..6552ed4ef8aea 100644
--- a/polkadot/runtime/test-runtime/Cargo.toml
+++ b/polkadot/runtime/test-runtime/Cargo.toml
@@ -11,14 +11,10 @@ license.workspace = true
 workspace = true
 
 [dependencies]
-bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] }
 parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive"] }
 log = { workspace = true }
-rustc-hex = { version = "2.1.0", default-features = false }
 scale-info = { version = "2.11.1", default-features = false, features = ["derive"] }
 serde = { workspace = true }
-serde_derive = { optional = true, workspace = true }
-smallvec = "1.8.0"
 
 authority-discovery-primitives = { package = "sp-authority-discovery", path = "../../../substrate/primitives/authority-discovery", default-features = false }
 babe-primitives = { package = "sp-consensus-babe", path = "../../../substrate/primitives/consensus/babe", default-features = false }
@@ -63,7 +59,6 @@ pallet-vesting = { path = "../../../substrate/frame/vesting", default-features =
 runtime-common = { package = "polkadot-runtime-common", path = "../common", default-features = false }
 primitives = { package = "polkadot-primitives", path = "../../primitives", default-features = false }
 pallet-xcm = { path = "../../xcm/pallet-xcm", default-features = false }
-polkadot-parachain-primitives = { path = "../../parachain", default-features = false }
 polkadot-runtime-parachains = { path = "../parachains", default-features = false }
 xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false }
 xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false }
@@ -92,7 +87,6 @@ std = [
 	"authority-discovery-primitives/std",
 	"babe-primitives/std",
 	"beefy-primitives/std",
-	"bitvec/std",
 	"block-builder-api/std",
 	"frame-election-provider-support/std",
 	"frame-executive/std",
@@ -118,14 +112,11 @@ std = [
 	"pallet-vesting/std",
 	"pallet-xcm/std",
 	"parity-scale-codec/std",
-	"polkadot-parachain-primitives/std",
 	"polkadot-runtime-parachains/std",
 	"primitives/std",
 	"runtime-common/std",
-	"rustc-hex/std",
 	"scale-info/std",
 	"serde/std",
-	"serde_derive",
 	"sp-api/std",
 	"sp-core/std",
 	"sp-genesis-builder/std",
@@ -157,7 +148,6 @@ runtime-benchmarks = [
 	"pallet-timestamp/runtime-benchmarks",
 	"pallet-vesting/runtime-benchmarks",
 	"pallet-xcm/runtime-benchmarks",
-	"polkadot-parachain-primitives/runtime-benchmarks",
 	"polkadot-runtime-parachains/runtime-benchmarks",
 	"primitives/runtime-benchmarks",
 	"runtime-common/runtime-benchmarks",
diff --git a/polkadot/runtime/test-runtime/constants/Cargo.toml b/polkadot/runtime/test-runtime/constants/Cargo.toml
index 2b387bbd3072a..5b8a4d7a051af 100644
--- a/polkadot/runtime/test-runtime/constants/Cargo.toml
+++ b/polkadot/runtime/test-runtime/constants/Cargo.toml
@@ -14,18 +14,12 @@ smallvec = "1.8.0"
 
 frame-support = { path = "../../../../substrate/frame/support", default-features = false }
 primitives = { package = "polkadot-primitives", path = "../../../primitives", default-features = false }
-runtime-common = { package = "polkadot-runtime-common", path = "../../common", default-features = false }
 sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false }
-sp-weights = { path = "../../../../substrate/primitives/weights", default-features = false }
-sp-core = { path = "../../../../substrate/primitives/core", default-features = false }
 
 [features]
 default = ["std"]
 std = [
 	"frame-support/std",
 	"primitives/std",
-	"runtime-common/std",
-	"sp-core/std",
 	"sp-runtime/std",
-	"sp-weights/std",
 ]
diff --git a/polkadot/runtime/test-runtime/src/xcm_config.rs b/polkadot/runtime/test-runtime/src/xcm_config.rs
index 8411b79f2529d..03e240e9c4846 100644
--- a/polkadot/runtime/test-runtime/src/xcm_config.rs
+++ b/polkadot/runtime/test-runtime/src/xcm_config.rs
@@ -24,8 +24,8 @@ use polkadot_runtime_parachains::FeeTracker;
 use runtime_common::xcm_sender::{ChildParachainRouter, PriceForMessageDelivery};
 use xcm::latest::prelude::*;
 use xcm_builder::{
-	AllowUnpaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor,
-	SignedAccountId32AsNative, SignedToAccountId32, WithUniqueTopic,
+	AllowUnpaidExecutionFrom, EnsureDecodableXcm, EnsureXcmOrigin, FixedWeightBounds,
+	FrameTransactionalProcessor, SignedAccountId32AsNative, SignedToAccountId32, WithUniqueTopic,
 };
 use xcm_executor::{
 	traits::{TransactAsset, WeightTrader},
@@ -82,8 +82,10 @@ pub type PriceForChildParachainDelivery = TestDeliveryPrice<FeeAssetId, super::D
 /// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our
 /// individual routers.
 pub type XcmRouter = WithUniqueTopic<
-	// Only one router so far - use DMP to communicate with child parachains.
-	ChildParachainRouter<super::Runtime, super::Xcm, PriceForChildParachainDelivery>,
+	EnsureDecodableXcm<
+		// Only one router so far - use DMP to communicate with child parachains.
+		ChildParachainRouter<super::Runtime, super::Xcm, PriceForChildParachainDelivery>,
+	>,
 >;
 
 pub type Barrier = AllowUnpaidExecutionFrom<Everything>;
diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs
index c831cd0246591..af23de17e3889 100644
--- a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs
+++ b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs
@@ -26,7 +26,9 @@ use frame_support::{
 use sp_core::H256;
 use sp_runtime::traits::{BlakeTwo256, IdentityLookup};
 use xcm::latest::prelude::*;
-use xcm_builder::{AllowUnpaidExecutionFrom, FrameTransactionalProcessor, MintLocation};
+use xcm_builder::{
+	AllowUnpaidExecutionFrom, EnsureDecodableXcm, FrameTransactionalProcessor, MintLocation,
+};
 
 type Block = frame_system::mocking::MockBlock<Test>;
 
@@ -121,7 +123,7 @@ parameter_types! {
 pub struct XcmConfig;
 impl xcm_executor::Config for XcmConfig {
 	type RuntimeCall = RuntimeCall;
-	type XcmSender = DevNull;
+	type XcmSender = EnsureDecodableXcm<DevNull>;
 	type AssetTransactor = AssetTransactor;
 	type OriginConverter = ();
 	type IsReserve = TrustedReserves;
diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs
index 534f7d85ea2e9..f2ace94502337 100644
--- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs
+++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs
@@ -30,7 +30,8 @@ use xcm_builder::{
 		AssetsInHolding, TestAssetExchanger, TestAssetLocker, TestAssetTrap,
 		TestSubscriptionService, TestUniversalAliases,
 	},
-	AliasForeignAccountId32, AllowUnpaidExecutionFrom, FrameTransactionalProcessor,
+	AliasForeignAccountId32, AllowUnpaidExecutionFrom, EnsureDecodableXcm,
+	FrameTransactionalProcessor,
 };
 use xcm_executor::traits::ConvertOrigin;
 
@@ -110,7 +111,7 @@ type Aliasers = AliasForeignAccountId32<OnlyParachains>;
 pub struct XcmConfig;
 impl xcm_executor::Config for XcmConfig {
 	type RuntimeCall = RuntimeCall;
-	type XcmSender = DevNull;
+	type XcmSender = EnsureDecodableXcm<DevNull>;
 	type AssetTransactor = NoAssetTransactor;
 	type OriginConverter = AlwaysSignedByDefault<RuntimeOrigin>;
 	type IsReserve = AllAssetLocationsPass;
diff --git a/polkadot/xcm/pallet-xcm/src/mock.rs b/polkadot/xcm/pallet-xcm/src/mock.rs
index 2cc228476ba8e..7fcbc881b438b 100644
--- a/polkadot/xcm/pallet-xcm/src/mock.rs
+++ b/polkadot/xcm/pallet-xcm/src/mock.rs
@@ -33,10 +33,11 @@ use xcm::prelude::*;
 use xcm_builder::{
 	AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
 	AllowTopLevelPaidExecutionFrom, Case, ChildParachainAsNative, ChildParachainConvertsVia,
-	ChildSystemParachainAsSuperuser, DescribeAllTerminal, FixedRateOfFungible, FixedWeightBounds,
-	FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, HashedDescription, IsConcrete,
-	MatchedConvertedConcreteId, NoChecking, SignedAccountId32AsNative, SignedToAccountId32,
-	SovereignSignedViaLocation, TakeWeightCredit, XcmFeeManagerFromComponents, XcmFeeToAccount,
+	ChildSystemParachainAsSuperuser, DescribeAllTerminal, EnsureDecodableXcm, FixedRateOfFungible,
+	FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter,
+	HashedDescription, IsConcrete, MatchedConvertedConcreteId, NoChecking,
+	SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
+	XcmFeeManagerFromComponents, XcmFeeToAccount,
 };
 use xcm_executor::{
 	traits::{Identity, JustTry},
@@ -488,7 +489,8 @@ pub type Barrier = (
 	AllowSubscriptionsFrom<Everything>,
 );
 
-pub type XcmRouter = (TestPaidForPara3000SendXcm, TestSendXcmErrX8, TestSendXcm);
+pub type XcmRouter =
+	EnsureDecodableXcm<(TestPaidForPara3000SendXcm, TestSendXcmErrX8, TestSendXcm)>;
 
 pub struct XcmConfig;
 impl xcm_executor::Config for XcmConfig {
diff --git a/polkadot/xcm/xcm-builder/src/tests/mock.rs b/polkadot/xcm/xcm-builder/src/tests/mock.rs
index 3d03ab054248d..7532b97d97b3b 100644
--- a/polkadot/xcm/xcm-builder/src/tests/mock.rs
+++ b/polkadot/xcm/xcm-builder/src/tests/mock.rs
@@ -19,6 +19,7 @@
 use crate::{
 	barriers::{AllowSubscriptionsFrom, RespectSuspension, TrailingSetTopicAsId},
 	test_utils::*,
+	EnsureDecodableXcm,
 };
 pub use crate::{
 	AliasForeignAccountId32, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses,
@@ -165,8 +166,8 @@ pub fn set_exporter_override(
 pub fn clear_exporter_override() {
 	EXPORTER_OVERRIDE.with(|x| x.replace(None));
 }
-pub struct TestMessageSender;
-impl SendXcm for TestMessageSender {
+pub struct TestMessageSenderImpl;
+impl SendXcm for TestMessageSenderImpl {
 	type Ticket = (Location, Xcm<()>, XcmHash);
 	fn validate(
 		dest: &mut Option<Location>,
@@ -183,6 +184,8 @@ impl SendXcm for TestMessageSender {
 		Ok(hash)
 	}
 }
+pub type TestMessageSender = EnsureDecodableXcm<TestMessageSenderImpl>;
+
 pub struct TestMessageExporter;
 impl ExportXcm for TestMessageExporter {
 	type Ticket = (NetworkId, u32, InteriorLocation, InteriorLocation, Xcm<()>, XcmHash);
diff --git a/polkadot/xcm/xcm-builder/tests/mock/mod.rs b/polkadot/xcm/xcm-builder/tests/mock/mod.rs
index f3cf5ab264902..474aa4ff9bc9e 100644
--- a/polkadot/xcm/xcm-builder/tests/mock/mod.rs
+++ b/polkadot/xcm/xcm-builder/tests/mock/mod.rs
@@ -35,9 +35,9 @@ use staging_xcm_builder as xcm_builder;
 use xcm_builder::{
 	AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom,
 	ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
-	FixedRateOfFungible, FixedWeightBounds, FungibleAdapter, IsChildSystemParachain, IsConcrete,
-	MintLocation, RespectSuspension, SignedAccountId32AsNative, SignedToAccountId32,
-	SovereignSignedViaLocation, TakeWeightCredit,
+	EnsureDecodableXcm, FixedRateOfFungible, FixedWeightBounds, FungibleAdapter,
+	IsChildSystemParachain, IsConcrete, MintLocation, RespectSuspension, SignedAccountId32AsNative,
+	SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
 };
 
 pub type AccountId = AccountId32;
@@ -68,6 +68,8 @@ impl SendXcm for TestSendXcm {
 	}
 }
 
+pub type TestXcmRouter = EnsureDecodableXcm<TestSendXcm>;
+
 // copied from kusama constants
 pub const UNITS: Balance = 1_000_000_000_000;
 pub const CENTS: Balance = UNITS / 30_000;
@@ -180,7 +182,7 @@ pub type TrustedTeleporters = (xcm_builder::Case<KusamaForAssetHub>,);
 pub struct XcmConfig;
 impl xcm_executor::Config for XcmConfig {
 	type RuntimeCall = RuntimeCall;
-	type XcmSender = TestSendXcm;
+	type XcmSender = TestXcmRouter;
 	type AssetTransactor = LocalAssetTransactor;
 	type OriginConverter = LocalOriginConverter;
 	type IsReserve = ();
@@ -215,7 +217,7 @@ impl pallet_xcm::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	type UniversalLocation = UniversalLocation;
 	type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
-	type XcmRouter = TestSendXcm;
+	type XcmRouter = TestXcmRouter;
 	// Anyone can execute XCM messages locally...
 	type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
 	type XcmExecuteFilter = Nothing;
diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain.rs b/polkadot/xcm/xcm-simulator/example/src/parachain.rs
index 86401d756af3b..830293d0a1630 100644
--- a/polkadot/xcm/xcm-simulator/example/src/parachain.rs
+++ b/polkadot/xcm/xcm-simulator/example/src/parachain.rs
@@ -40,10 +40,10 @@ use polkadot_parachain_primitives::primitives::{
 use xcm::{latest::prelude::*, VersionedXcm};
 use xcm_builder::{
 	Account32Hash, AccountId32Aliases, AllowUnpaidExecutionFrom, ConvertedConcreteId,
-	EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FrameTransactionalProcessor,
-	FungibleAdapter, IsConcrete, NativeAsset, NoChecking, NonFungiblesAdapter, ParentIsPreset,
-	SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32,
-	SovereignSignedViaLocation,
+	EnsureDecodableXcm, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds,
+	FrameTransactionalProcessor, FungibleAdapter, IsConcrete, NativeAsset, NoChecking,
+	NonFungiblesAdapter, ParentIsPreset, SiblingParachainConvertsVia, SignedAccountId32AsNative,
+	SignedToAccountId32, SovereignSignedViaLocation,
 };
 use xcm_executor::{
 	traits::{ConvertLocation, JustTry},
@@ -212,7 +212,7 @@ pub type LocalAssetTransactor = (
 	>,
 );
 
-pub type XcmRouter = super::ParachainXcmRouter<MsgQueue>;
+pub type XcmRouter = EnsureDecodableXcm<super::ParachainXcmRouter<MsgQueue>>;
 pub type Barrier = AllowUnpaidExecutionFrom<Everything>;
 
 parameter_types! {
diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs
index 286d0038e187a..16a4eb6d3bc35 100644
--- a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs
+++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs
@@ -168,7 +168,7 @@ parameter_types! {
 	pub const MaxAssetsIntoHolding: u32 = 64;
 }
 
-pub type XcmRouter = super::RelayChainXcmRouter;
+pub type XcmRouter = EnsureDecodableXcm<super::RelayChainXcmRouter>;
 pub type Barrier = AllowUnpaidExecutionFrom<Everything>;
 
 pub struct XcmConfig;

From feaf731ef208e8522b0772c878594efe556a53fe Mon Sep 17 00:00:00 2001
From: Branislav Kontur <bkontur@gmail.com>
Date: Thu, 18 Apr 2024 11:55:28 +0200
Subject: [PATCH 3/6] Update polkadot/xcm/xcm-builder/src/routing.rs

Co-authored-by: Adrian Catangiu <adrian@parity.io>
---
 polkadot/xcm/xcm-builder/src/routing.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/polkadot/xcm/xcm-builder/src/routing.rs b/polkadot/xcm/xcm-builder/src/routing.rs
index 4b4dee9f1312f..243f8040e6614 100644
--- a/polkadot/xcm/xcm-builder/src/routing.rs
+++ b/polkadot/xcm/xcm-builder/src/routing.rs
@@ -147,7 +147,7 @@ impl EnsureDelivery for Tuple {
 /// routers can manipulate the `message`, we cannot access the final XCM due to the generic
 /// `Inner::Ticket`. Therefore, this router aims to validate at least the passed `message`.
 ///
-/// NOTE: For testing purposes.
+/// NOTE: For use in mock runtimes which don't have the DMP/UMP/HRMP XCM validations.
 pub struct EnsureDecodableXcm<Inner>(sp_std::marker::PhantomData<Inner>);
 impl<Inner: SendXcm> SendXcm for EnsureDecodableXcm<Inner> {
 	type Ticket = Inner::Ticket;

From 5080212a55868500a174ca4d6d0d6133a3e5b4b1 Mon Sep 17 00:00:00 2001
From: Branislav Kontur <bkontur@gmail.com>
Date: Thu, 18 Apr 2024 12:40:58 +0200
Subject: [PATCH 4/6] Fix import

---
 polkadot/xcm/xcm-simulator/example/src/relay_chain.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs
index 16a4eb6d3bc35..5273d47604100 100644
--- a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs
+++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs
@@ -36,9 +36,9 @@ use xcm::latest::prelude::*;
 use xcm_builder::{
 	Account32Hash, AccountId32Aliases, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex,
 	ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
-	ConvertedConcreteId, FixedRateOfFungible, FixedWeightBounds, FrameTransactionalProcessor,
-	FungibleAdapter, IsConcrete, NoChecking, NonFungiblesAdapter, SignedAccountId32AsNative,
-	SignedToAccountId32, SovereignSignedViaLocation,
+	ConvertedConcreteId, EnsureDecodableXcm, FixedRateOfFungible, FixedWeightBounds,
+	FrameTransactionalProcessor, FungibleAdapter, IsConcrete, NoChecking, NonFungiblesAdapter,
+	SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation,
 };
 use xcm_executor::{traits::JustTry, Config, XcmExecutor};
 

From f7e94f42cf592d3c8d580a1969aaf05b6e873793 Mon Sep 17 00:00:00 2001
From: Branislav Kontur <bkontur@gmail.com>
Date: Thu, 18 Apr 2024 22:17:18 +0200
Subject: [PATCH 5/6] Fixed XCM benchmarks to generate holding register data

import
---
 .../src/fungible/benchmarking.rs              | 26 ++++++++++----
 .../src/fungible/mock.rs                      |  7 ++--
 .../src/generic/benchmarking.rs               | 36 ++++++++++++-------
 .../pallet-xcm-benchmarks/src/generic/mock.rs |  5 ++-
 polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs | 13 ++++---
 5 files changed, 57 insertions(+), 30 deletions(-)

diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs
index 4b77199069d34..d99da9184b5d8 100644
--- a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs
+++ b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs
@@ -146,8 +146,6 @@ benchmarks_instance_pallet! {
 
 	initiate_reserve_withdraw {
 		let (sender_account, sender_location) = account_and_location::<T>(1);
-		let holding = T::worst_case_holding(1);
-		let assets_filter = AssetFilter::Definite(holding.clone().into_inner().into_iter().take(MAX_ITEMS_IN_ASSETS).collect::<Vec<_>>().into());
 		let reserve = T::valid_destination().map_err(|_| BenchmarkError::Skip)?;
 
 		let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery(
@@ -157,15 +155,29 @@ benchmarks_instance_pallet! {
 		);
 		let sender_account_balance_before = T::TransactAsset::balance(&sender_account);
 
+		// generate holding and add possible required fees
+		let holding = if let Some(expected_assets_in_holding) = expected_assets_in_holding {
+			let mut holding = T::worst_case_holding(1 + expected_assets_in_holding.len() as u32);
+			for a in expected_assets_in_holding.into_inner() {
+				holding.push(a);
+			}
+			holding
+		} else {
+			T::worst_case_holding(1)
+		};
+
 		let mut executor = new_executor::<T>(sender_location);
-		executor.set_holding(holding.into());
+		executor.set_holding(holding.clone().into());
 		if let Some(expected_fees_mode) = expected_fees_mode {
 			executor.set_fees_mode(expected_fees_mode);
 		}
-		if let Some(expected_assets_in_holding) = expected_assets_in_holding {
-			executor.set_holding(expected_assets_in_holding.into());
-		}
-		let instruction = Instruction::InitiateReserveWithdraw { assets: assets_filter, reserve, xcm: Xcm(vec![]) };
+
+		let instruction = Instruction::InitiateReserveWithdraw {
+			// Worst case is looking through all holdings for every asset explicitly - respecting the limit `MAX_ITEMS_IN_ASSETS`.
+			assets: Definite(holding.into_inner().into_iter().take(MAX_ITEMS_IN_ASSETS).collect::<Vec<_>>().into()),
+			reserve,
+			xcm: Xcm(vec![])
+		};
 		let xcm = Xcm(vec![instruction]);
 	}: {
 		executor.bench_process(xcm)?;
diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs
index af23de17e3889..d93b60a6e0256 100644
--- a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs
+++ b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs
@@ -16,7 +16,7 @@
 
 //! A mock runtime for XCM benchmarking.
 
-use crate::{fungible as xcm_balances_benchmark, mock::*};
+use crate::{fungible as xcm_balances_benchmark, generate_holding_assets, mock::*};
 use frame_benchmarking::BenchmarkError;
 use frame_support::{
 	derive_impl, parameter_types,
@@ -162,9 +162,8 @@ impl crate::Config for Test {
 		Ok(valid_destination)
 	}
 	fn worst_case_holding(depositable_count: u32) -> Assets {
-		crate::mock_worst_case_holding(
-			depositable_count,
-			<XcmConfig as xcm_executor::Config>::MaxAssetsIntoHolding::get(),
+		generate_holding_assets(
+			<XcmConfig as xcm_executor::Config>::MaxAssetsIntoHolding::get() - depositable_count,
 		)
 	}
 }
diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs
index 8c6ed4b5d0e02..760b21f93566e 100644
--- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs
+++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs
@@ -19,9 +19,9 @@ use crate::{account_and_location, new_executor, EnsureDelivery, XcmCallOf};
 use codec::Encode;
 use frame_benchmarking::{benchmarks, BenchmarkError};
 use frame_support::{dispatch::GetDispatchInfo, traits::fungible::Inspect};
-use sp_std::vec;
+use sp_std::{prelude::*, vec};
 use xcm::{
-	latest::{prelude::*, MaxDispatchErrorLen, MaybeErrorCode, Weight},
+	latest::{prelude::*, MaxDispatchErrorLen, MaybeErrorCode, Weight, MAX_ITEMS_IN_ASSETS},
 	DoubleEncoded,
 };
 use xcm_executor::{
@@ -32,7 +32,6 @@ use xcm_executor::{
 benchmarks! {
 	report_holding {
 		let (sender_account, sender_location) = account_and_location::<T>(1);
-		let holding = T::worst_case_holding(0);
 		let destination = T::valid_destination().map_err(|_| BenchmarkError::Skip)?;
 
 		let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery(
@@ -42,14 +41,22 @@ benchmarks! {
 		);
 		let sender_account_balance_before = T::TransactAsset::balance(&sender_account);
 
+		// generate holding and add possible required fees
+		let holding = if let Some(expected_assets_in_holding) = expected_assets_in_holding {
+			let mut holding = T::worst_case_holding(expected_assets_in_holding.len() as u32);
+			for a in expected_assets_in_holding.into_inner() {
+				holding.push(a);
+			}
+			holding
+		} else {
+			T::worst_case_holding(0)
+		};
+
 		let mut executor = new_executor::<T>(sender_location);
 		executor.set_holding(holding.clone().into());
 		if let Some(expected_fees_mode) = expected_fees_mode {
 			executor.set_fees_mode(expected_fees_mode);
 		}
-		if let Some(expected_assets_in_holding) = expected_assets_in_holding {
-			executor.set_holding(expected_assets_in_holding.into());
-		}
 
 		let instruction = Instruction::<XcmCallOf<T>>::ReportHolding {
 			response_info: QueryResponseInfo {
@@ -57,8 +64,8 @@ benchmarks! {
 				query_id: Default::default(),
 				max_weight: Weight::MAX,
 			},
-			// Worst case is looking through all holdings for every asset explicitly.
-			assets: Definite(holding),
+			// Worst case is looking through all holdings for every asset explicitly - respecting the limit `MAX_ITEMS_IN_ASSETS`.
+			assets: Definite(holding.into_inner().into_iter().take(MAX_ITEMS_IN_ASSETS).collect::<Vec<_>>().into()),
 		};
 
 		let xcm = Xcm(vec![instruction]);
@@ -612,14 +619,19 @@ benchmarks! {
 		let sender_account = T::AccountIdConverter::convert_location(&owner).unwrap();
 		let sender_account_balance_before = T::TransactAsset::balance(&sender_account);
 
+		// generate holding and add possible required fees
+		let mut holding: Assets = asset.clone().into();
+		if let Some(expected_assets_in_holding) = expected_assets_in_holding {
+			for a in expected_assets_in_holding.into_inner() {
+				holding.push(a);
+			}
+		};
+
 		let mut executor = new_executor::<T>(owner);
-		executor.set_holding(asset.clone().into());
+		executor.set_holding(holding.into());
 		if let Some(expected_fees_mode) = expected_fees_mode {
 			executor.set_fees_mode(expected_fees_mode);
 		}
-		if let Some(expected_assets_in_holding) = expected_assets_in_holding {
-			executor.set_holding(expected_assets_in_holding.into());
-		}
 
 		let instruction = Instruction::LockAsset { asset, unlocker };
 		let xcm = Xcm(vec![instruction]);
diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs
index f2ace94502337..5732d52962e3d 100644
--- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs
+++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs
@@ -162,9 +162,8 @@ impl crate::Config for Test {
 		Ok(valid_destination)
 	}
 	fn worst_case_holding(depositable_count: u32) -> Assets {
-		crate::mock_worst_case_holding(
-			depositable_count,
-			<XcmConfig as xcm_executor::Config>::MaxAssetsIntoHolding::get(),
+		generate_holding_assets(
+			<XcmConfig as xcm_executor::Config>::MaxAssetsIntoHolding::get() - depositable_count,
 		)
 	}
 }
diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs
index 63ed0ac0ca736..a43f27bf47e72 100644
--- a/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs
+++ b/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs
@@ -50,6 +50,8 @@ pub trait Config: frame_system::Config {
 	fn valid_destination() -> Result<Location, BenchmarkError>;
 
 	/// Worst case scenario for a holding account in this runtime.
+	/// - `depositable_count` specifies the count of assets we plan to add to the holding on top of
+	///   those generated by the `worst_case_holding` implementation.
 	fn worst_case_holding(depositable_count: u32) -> Assets;
 }
 
@@ -64,19 +66,22 @@ pub type AssetTransactorOf<T> = <<T as Config>::XcmConfig as XcmConfig>::AssetTr
 /// The call type of executor's config. Should eventually resolve to the same overarching call type.
 pub type XcmCallOf<T> = <<T as Config>::XcmConfig as XcmConfig>::RuntimeCall;
 
-pub fn mock_worst_case_holding(depositable_count: u32, max_assets: u32) -> Assets {
+pub fn generate_holding_assets(max_assets: u32) -> Assets {
 	let fungibles_amount: u128 = 100;
-	let holding_fungibles = max_assets / 2 - depositable_count;
-	let holding_non_fungibles = holding_fungibles;
+	let holding_fungibles = max_assets / 2;
+	let holding_non_fungibles = max_assets - holding_fungibles - 1; // -1 because of adding `Here` asset
+																// add count of `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
 			}
 			.into()
 		})
+		// add one more `Here` asset
 		.chain(core::iter::once(Asset { id: AssetId(Here.into()), fun: Fungible(u128::MAX) }))
+		// add count of `holding_non_fungibles`
 		.chain((0..holding_non_fungibles).map(|i| Asset {
 			id: AssetId(GeneralIndex(i as u128).into()),
 			fun: NonFungible(asset_instance_from(i)),

From d08c4404a5e1a93f710821bb91904572848f2811 Mon Sep 17 00:00:00 2001
From: Branislav Kontur <bkontur@gmail.com>
Date: Tue, 23 Apr 2024 12:26:37 +0200
Subject: [PATCH 6/6] Removed `ensure_encode_decode` and replaced by
 `VersionedXcm::validate_xcm_nesting`

---
 .../runtime/test-runtime/src/xcm_config.rs    | 10 ++--
 polkadot/xcm/xcm-builder/src/routing.rs       | 46 +++----------------
 2 files changed, 11 insertions(+), 45 deletions(-)

diff --git a/polkadot/runtime/test-runtime/src/xcm_config.rs b/polkadot/runtime/test-runtime/src/xcm_config.rs
index 03e240e9c4846..8411b79f2529d 100644
--- a/polkadot/runtime/test-runtime/src/xcm_config.rs
+++ b/polkadot/runtime/test-runtime/src/xcm_config.rs
@@ -24,8 +24,8 @@ use polkadot_runtime_parachains::FeeTracker;
 use runtime_common::xcm_sender::{ChildParachainRouter, PriceForMessageDelivery};
 use xcm::latest::prelude::*;
 use xcm_builder::{
-	AllowUnpaidExecutionFrom, EnsureDecodableXcm, EnsureXcmOrigin, FixedWeightBounds,
-	FrameTransactionalProcessor, SignedAccountId32AsNative, SignedToAccountId32, WithUniqueTopic,
+	AllowUnpaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor,
+	SignedAccountId32AsNative, SignedToAccountId32, WithUniqueTopic,
 };
 use xcm_executor::{
 	traits::{TransactAsset, WeightTrader},
@@ -82,10 +82,8 @@ pub type PriceForChildParachainDelivery = TestDeliveryPrice<FeeAssetId, super::D
 /// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our
 /// individual routers.
 pub type XcmRouter = WithUniqueTopic<
-	EnsureDecodableXcm<
-		// Only one router so far - use DMP to communicate with child parachains.
-		ChildParachainRouter<super::Runtime, super::Xcm, PriceForChildParachainDelivery>,
-	>,
+	// Only one router so far - use DMP to communicate with child parachains.
+	ChildParachainRouter<super::Runtime, super::Xcm, PriceForChildParachainDelivery>,
 >;
 
 pub type Barrier = AllowUnpaidExecutionFrom<Everything>;
diff --git a/polkadot/xcm/xcm-builder/src/routing.rs b/polkadot/xcm/xcm-builder/src/routing.rs
index 243f8040e6614..921b9ac5922ea 100644
--- a/polkadot/xcm/xcm-builder/src/routing.rs
+++ b/polkadot/xcm/xcm-builder/src/routing.rs
@@ -157,8 +157,13 @@ impl<Inner: SendXcm> SendXcm for EnsureDecodableXcm<Inner> {
 		message: &mut Option<Xcm<()>>,
 	) -> SendResult<Self::Ticket> {
 		if let Some(msg) = message {
-			if let Some(e) = Self::ensure_encode_decode(msg) {
-				return Err(e)
+			let versioned_xcm = VersionedXcm::<()>::from(msg.clone());
+			if versioned_xcm.validate_xcm_nesting().is_err() {
+				log::error!(
+					target: "xcm::validate_xcm_nesting",
+					"EnsureDecodableXcm validate_xcm_nesting error for \nversioned_xcm: {versioned_xcm:?}\nbased on xcm: {msg:?}"
+				);
+				return Err(SendError::Transport("EnsureDecodableXcm validate_xcm_nesting error"))
 			}
 		}
 		Inner::validate(destination, message)
@@ -168,40 +173,3 @@ impl<Inner: SendXcm> SendXcm for EnsureDecodableXcm<Inner> {
 		Inner::deliver(ticket)
 	}
 }
-impl<Inner> EnsureDecodableXcm<Inner> {
-	fn ensure_encode_decode(xcm: &Xcm<()>) -> Option<SendError> {
-		use parity_scale_codec::Decode;
-		let encoded = xcm.encode();
-		match Xcm::<()>::decode(&mut &encoded[..]) {
-			Ok(decoded_xcm) => match decoded_xcm.eq(xcm) {
-				true => None,
-				false => {
-					log::error!(target: "xcm::test_utils", "EnsureDecodableXcm `decoded_xcm` does not match `xcm` - \ndecoded_xcm: {decoded_xcm:?} \nxcm:{xcm:?}");
-					Some(SendError::Transport("Decoded xcm does not match xcm!"))
-				},
-			},
-			Err(e) => {
-				log::error!(target: "xcm::test_utils", "EnsureDecodableXcm decode error: {e:?} occurred for xcm: {xcm:?}");
-				Some(SendError::Transport("Failed to encode/decode xcm!"))
-			},
-		}
-	}
-}
-
-#[test]
-fn ensure_encode_decode_works() {
-	let good_fun = vec![(Here, 10).into(), (Parent, 10).into()];
-	assert!(Assets::from_sorted_and_deduplicated(good_fun.clone()).is_ok());
-
-	let good_xcm = Xcm(vec![ReserveAssetDeposited(Assets::from(good_fun.clone()))]);
-	assert!(EnsureDecodableXcm::<()>::ensure_encode_decode(&good_xcm).is_none());
-
-	let bad_xcm = Xcm(vec![
-		ReserveAssetDeposited(Assets::from(good_fun));
-		(xcm::latest::MAX_INSTRUCTIONS_TO_DECODE + 1) as usize
-	]);
-	assert_eq!(
-		EnsureDecodableXcm::<()>::ensure_encode_decode(&bad_xcm),
-		Some(SendError::Transport("Failed to encode/decode xcm!"))
-	);
-}