From 1266de3919dcc9b100c80752ad0158e26ff0fabb Mon Sep 17 00:00:00 2001
From: Serban Iorga <serban@parity.io>
Date: Mon, 4 Dec 2023 13:16:20 +0100
Subject: [PATCH] Cleanup XCMP `QueueConfigData` (#2142)

Removes obsolete fields from the `QueueConfigData` structure. For the
remaining fields, if they use the old defaults, we replace them with the
new defaults.

Resolves: https://github.com/paritytech/polkadot-sdk/issues/1795
---
 cumulus/pallets/xcmp-queue/src/lib.rs         |  24 +-
 cumulus/pallets/xcmp-queue/src/migration.rs   | 381 +++++++++++++-----
 .../assets/asset-hub-rococo/src/lib.rs        |   8 +-
 .../assets/asset-hub-westend/src/lib.rs       |   2 +
 .../bridge-hubs/bridge-hub-rococo/src/lib.rs  |   1 +
 .../bridge-hubs/bridge-hub-westend/src/lib.rs |   2 +
 .../collectives-westend/src/lib.rs            |   2 +
 .../contracts/contracts-rococo/src/lib.rs     |   4 +-
 prdoc/pr_2142.prdoc                           |  14 +
 9 files changed, 308 insertions(+), 130 deletions(-)
 create mode 100644 prdoc/pr_2142.prdoc

diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs
index d687f83d8b3..d3443163d08 100644
--- a/cumulus/pallets/xcmp-queue/src/lib.rs
+++ b/cumulus/pallets/xcmp-queue/src/lib.rs
@@ -60,7 +60,7 @@ use cumulus_primitives_core::{
 use frame_support::{
 	defensive, defensive_assert,
 	traits::{EnqueueMessage, EnsureOrigin, Get, QueueFootprint, QueuePausedQuery},
-	weights::{constants::WEIGHT_REF_TIME_PER_MILLIS, Weight, WeightMeter},
+	weights::{Weight, WeightMeter},
 	BoundedVec,
 };
 use pallet_message_queue::OnQueueChanged;
@@ -255,7 +255,7 @@ pub mod pallet {
 				return meter.consumed()
 			}
 
-			migration::lazy_migrate_inbound_queue::<T>();
+			migration::v3::lazy_migrate_inbound_queue::<T>();
 
 			meter.consumed()
 		}
@@ -387,36 +387,16 @@ pub struct QueueConfigData {
 	/// The number of pages which the queue must be reduced to before it signals that
 	/// message sending may recommence after it has been suspended.
 	resume_threshold: u32,
-	/// UNUSED - The amount of remaining weight under which we stop processing messages.
-	#[deprecated(note = "Will be removed")]
-	threshold_weight: Weight,
-	/// UNUSED - The speed to which the available weight approaches the maximum weight. A lower
-	/// number results in a faster progression. A value of 1 makes the entire weight available
-	/// initially.
-	#[deprecated(note = "Will be removed")]
-	weight_restrict_decay: Weight,
-	/// UNUSED - The maximum amount of weight any individual message may consume. Messages above
-	/// this weight go into the overweight queue and may only be serviced explicitly.
-	#[deprecated(note = "Will be removed")]
-	xcmp_max_individual_weight: Weight,
 }
 
 impl Default for QueueConfigData {
 	fn default() -> Self {
 		// NOTE that these default values are only used on genesis. They should give a rough idea of
 		// what to set these values to, but is in no way a requirement.
-		#![allow(deprecated)]
 		Self {
 			drop_threshold: 48,    // 64KiB * 48 = 3MiB
 			suspend_threshold: 32, // 64KiB * 32 = 2MiB
 			resume_threshold: 8,   // 64KiB * 8 = 512KiB
-			// unused:
-			threshold_weight: Weight::from_parts(100_000, 0),
-			weight_restrict_decay: Weight::from_parts(2, 0),
-			xcmp_max_individual_weight: Weight::from_parts(
-				20u64 * WEIGHT_REF_TIME_PER_MILLIS,
-				DEFAULT_POV_SIZE,
-			),
 		}
 	}
 }
diff --git a/cumulus/pallets/xcmp-queue/src/migration.rs b/cumulus/pallets/xcmp-queue/src/migration.rs
index 6d7f434b041..6c86c3011d2 100644
--- a/cumulus/pallets/xcmp-queue/src/migration.rs
+++ b/cumulus/pallets/xcmp-queue/src/migration.rs
@@ -16,7 +16,7 @@
 
 //! A module that is responsible for migration of storage.
 
-use crate::{Config, OverweightIndex, Pallet, ParaId, QueueConfig, DEFAULT_POV_SIZE};
+use crate::{Config, OverweightIndex, Pallet, QueueConfig, QueueConfigData, DEFAULT_POV_SIZE};
 use cumulus_primitives_core::XcmpMessageFormat;
 use frame_support::{
 	pallet_prelude::*,
@@ -25,37 +25,17 @@ use frame_support::{
 };
 
 /// The current storage version.
-pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(3);
+pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(4);
 
 pub const LOG: &str = "runtime::xcmp-queue-migration";
 
-/// Migrates the pallet storage to the most recent version.
-pub struct MigrationToV3<T: Config>(PhantomData<T>);
-
-impl<T: Config> OnRuntimeUpgrade for MigrationToV3<T> {
-	fn on_runtime_upgrade() -> Weight {
-		let mut weight = T::DbWeight::get().reads(1);
-
-		if StorageVersion::get::<Pallet<T>>() == 1 {
-			weight.saturating_accrue(migrate_to_v2::<T>());
-			StorageVersion::new(2).put::<Pallet<T>>();
-			weight.saturating_accrue(T::DbWeight::get().writes(1));
-		}
-
-		if StorageVersion::get::<Pallet<T>>() == 2 {
-			weight.saturating_accrue(migrate_to_v3::<T>());
-			StorageVersion::new(3).put::<Pallet<T>>();
-			weight.saturating_accrue(T::DbWeight::get().writes(1));
-		}
-
-		weight
-	}
-}
-
 mod v1 {
 	use super::*;
 	use codec::{Decode, Encode};
 
+	#[frame_support::storage_alias]
+	pub(crate) type QueueConfig<T: Config> = StorageValue<Pallet<T>, QueueConfigData, ValueQuery>;
+
 	#[derive(Encode, Decode, Debug)]
 	pub struct QueueConfigData {
 		pub suspend_threshold: u32,
@@ -80,6 +60,84 @@ mod v1 {
 	}
 }
 
+pub mod v2 {
+	use super::*;
+
+	#[frame_support::storage_alias]
+	pub(crate) type QueueConfig<T: Config> = StorageValue<Pallet<T>, QueueConfigData, ValueQuery>;
+
+	#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
+	pub struct QueueConfigData {
+		pub suspend_threshold: u32,
+		pub drop_threshold: u32,
+		pub resume_threshold: u32,
+		pub threshold_weight: Weight,
+		pub weight_restrict_decay: Weight,
+		pub xcmp_max_individual_weight: Weight,
+	}
+
+	impl Default for QueueConfigData {
+		fn default() -> Self {
+			Self {
+				suspend_threshold: 2,
+				drop_threshold: 5,
+				resume_threshold: 1,
+				threshold_weight: Weight::from_parts(100_000, 0),
+				weight_restrict_decay: Weight::from_parts(2, 0),
+				xcmp_max_individual_weight: Weight::from_parts(
+					20u64 * WEIGHT_REF_TIME_PER_MILLIS,
+					DEFAULT_POV_SIZE,
+				),
+			}
+		}
+	}
+
+	/// Migrates `QueueConfigData` from v1 (using only reference time weights) to v2 (with
+	/// 2D weights).
+	pub struct UncheckedMigrationToV2<T: Config>(PhantomData<T>);
+
+	impl<T: Config> OnRuntimeUpgrade for UncheckedMigrationToV2<T> {
+		#[allow(deprecated)]
+		fn on_runtime_upgrade() -> Weight {
+			let translate = |pre: v1::QueueConfigData| -> v2::QueueConfigData {
+				v2::QueueConfigData {
+					suspend_threshold: pre.suspend_threshold,
+					drop_threshold: pre.drop_threshold,
+					resume_threshold: pre.resume_threshold,
+					threshold_weight: Weight::from_parts(pre.threshold_weight, 0),
+					weight_restrict_decay: Weight::from_parts(pre.weight_restrict_decay, 0),
+					xcmp_max_individual_weight: Weight::from_parts(
+						pre.xcmp_max_individual_weight,
+						DEFAULT_POV_SIZE,
+					),
+				}
+			};
+
+			if v2::QueueConfig::<T>::translate(|pre| pre.map(translate)).is_err() {
+				log::error!(
+					target: crate::LOG_TARGET,
+					"unexpected error when performing translation of the QueueConfig type \
+					during storage upgrade to v2"
+				);
+			}
+
+			T::DbWeight::get().reads_writes(1, 1)
+		}
+	}
+
+	/// [`UncheckedMigrationToV2`] wrapped in a
+	/// [`VersionedMigration`](frame_support::migrations::VersionedMigration), ensuring the
+	/// migration is only performed when on-chain version is 1.
+	#[allow(dead_code)]
+	pub type MigrationToV2<T> = frame_support::migrations::VersionedMigration<
+		1,
+		2,
+		UncheckedMigrationToV2<T>,
+		Pallet<T>,
+		<T as frame_system::Config>::DbWeight,
+	>;
+}
+
 pub mod v3 {
 	use super::*;
 	use crate::*;
@@ -101,6 +159,10 @@ pub mod v3 {
 		OptionQuery,
 	>;
 
+	#[frame_support::storage_alias]
+	pub(crate) type QueueConfig<T: Config> =
+		StorageValue<Pallet<T>, v2::QueueConfigData, ValueQuery>;
+
 	#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
 	pub struct InboundChannelDetails {
 		/// The `ParaId` of the parachain that this channel is connected with.
@@ -121,98 +183,135 @@ pub mod v3 {
 		Ok,
 		Suspended,
 	}
-}
 
-/// Migrates `QueueConfigData` from v1 (using only reference time weights) to v2 (with
-/// 2D weights).
-///
-/// NOTE: Only use this function if you know what you're doing. Default to using
-/// `migrate_to_latest`.
-#[allow(deprecated)]
-pub fn migrate_to_v2<T: Config>() -> Weight {
-	let translate = |pre: v1::QueueConfigData| -> super::QueueConfigData {
-		super::QueueConfigData {
-			suspend_threshold: pre.suspend_threshold,
-			drop_threshold: pre.drop_threshold,
-			resume_threshold: pre.resume_threshold,
-			threshold_weight: Weight::from_parts(pre.threshold_weight, 0),
-			weight_restrict_decay: Weight::from_parts(pre.weight_restrict_decay, 0),
-			xcmp_max_individual_weight: Weight::from_parts(
-				pre.xcmp_max_individual_weight,
-				DEFAULT_POV_SIZE,
-			),
-		}
-	};
+	/// Migrates the pallet storage to v3.
+	pub struct UncheckedMigrationToV3<T: Config>(PhantomData<T>);
 
-	if QueueConfig::<T>::translate(|pre| pre.map(translate)).is_err() {
-		log::error!(
-			target: super::LOG_TARGET,
-			"unexpected error when performing translation of the QueueConfig type during storage upgrade to v2"
-		);
+	impl<T: Config> OnRuntimeUpgrade for UncheckedMigrationToV3<T> {
+		fn on_runtime_upgrade() -> Weight {
+			#[frame_support::storage_alias]
+			type Overweight<T: Config> =
+				CountedStorageMap<Pallet<T>, Twox64Concat, OverweightIndex, ParaId>;
+			let overweight_messages = Overweight::<T>::initialize_counter() as u64;
+
+			T::DbWeight::get().reads_writes(overweight_messages, 1)
+		}
 	}
 
-	T::DbWeight::get().reads_writes(1, 1)
-}
+	/// [`UncheckedMigrationToV3`] wrapped in a
+	/// [`VersionedMigration`](frame_support::migrations::VersionedMigration), ensuring the
+	/// migration is only performed when on-chain version is 2.
+	pub type MigrationToV3<T> = frame_support::migrations::VersionedMigration<
+		2,
+		3,
+		UncheckedMigrationToV3<T>,
+		Pallet<T>,
+		<T as frame_system::Config>::DbWeight,
+	>;
 
-pub fn migrate_to_v3<T: Config>() -> Weight {
-	#[frame_support::storage_alias]
-	type Overweight<T: Config> =
-		CountedStorageMap<Pallet<T>, Twox64Concat, OverweightIndex, ParaId>;
-	let overweight_messages = Overweight::<T>::initialize_counter() as u64;
+	pub fn lazy_migrate_inbound_queue<T: Config>() {
+		let Some(mut states) = v3::InboundXcmpStatus::<T>::get() else {
+			log::debug!(target: LOG, "Lazy migration finished: item gone");
+			return
+		};
+		let Some(ref mut next) = states.first_mut() else {
+			log::debug!(target: LOG, "Lazy migration finished: item empty");
+			v3::InboundXcmpStatus::<T>::kill();
+			return
+		};
+		log::debug!(
+			"Migrating inbound HRMP channel with sibling {:?}, msgs left {}.",
+			next.sender,
+			next.message_metadata.len()
+		);
+		// We take the last element since the MQ is a FIFO and we want to keep the order.
+		let Some((block_number, format)) = next.message_metadata.pop() else {
+			states.remove(0);
+			v3::InboundXcmpStatus::<T>::put(states);
+			return
+		};
+		if format != XcmpMessageFormat::ConcatenatedVersionedXcm {
+			log::warn!(target: LOG,
+				"Dropping message with format {:?} (not ConcatenatedVersionedXcm)",
+				format
+			);
+			v3::InboundXcmpMessages::<T>::remove(&next.sender, &block_number);
+			v3::InboundXcmpStatus::<T>::put(states);
+			return
+		}
 
-	T::DbWeight::get().reads_writes(overweight_messages, 1)
-}
+		let Some(msg) = v3::InboundXcmpMessages::<T>::take(&next.sender, &block_number) else {
+			defensive!("Storage corrupted: HRMP message missing:", (next.sender, block_number));
+			v3::InboundXcmpStatus::<T>::put(states);
+			return
+		};
 
-pub fn lazy_migrate_inbound_queue<T: Config>() {
-	let Some(mut states) = v3::InboundXcmpStatus::<T>::get() else {
-		log::debug!(target: LOG, "Lazy migration finished: item gone");
-		return
-	};
-	let Some(ref mut next) = states.first_mut() else {
-		log::debug!(target: LOG, "Lazy migration finished: item empty");
-		v3::InboundXcmpStatus::<T>::kill();
-		return
-	};
-	log::debug!(
-		"Migrating inbound HRMP channel with sibling {:?}, msgs left {}.",
-		next.sender,
-		next.message_metadata.len()
-	);
-	// We take the last element since the MQ is a FIFO and we want to keep the order.
-	let Some((block_number, format)) = next.message_metadata.pop() else {
-		states.remove(0);
-		v3::InboundXcmpStatus::<T>::put(states);
-		return
-	};
-	if format != XcmpMessageFormat::ConcatenatedVersionedXcm {
-		log::warn!(target: LOG,
-			"Dropping message with format {:?} (not ConcatenatedVersionedXcm)",
-			format
-		);
-		v3::InboundXcmpMessages::<T>::remove(&next.sender, &block_number);
+		let Ok(msg): Result<BoundedVec<_, _>, _> = msg.try_into() else {
+			log::error!(target: LOG, "Message dropped: too big");
+			v3::InboundXcmpStatus::<T>::put(states);
+			return
+		};
+
+		// Finally! We have a proper message.
+		T::XcmpQueue::enqueue_message(msg.as_bounded_slice(), next.sender);
+		log::debug!(target: LOG, "Migrated HRMP message to MQ: {:?}", (next.sender, block_number));
 		v3::InboundXcmpStatus::<T>::put(states);
-		return
 	}
+}
 
-	let Some(msg) = v3::InboundXcmpMessages::<T>::take(&next.sender, &block_number) else {
-		defensive!("Storage corrupted: HRMP message missing:", (next.sender, block_number));
-		v3::InboundXcmpStatus::<T>::put(states);
-		return
-	};
+pub mod v4 {
+	use super::*;
 
-	let Ok(msg): Result<BoundedVec<_, _>, _> = msg.try_into() else {
-		log::error!(target: LOG, "Message dropped: too big");
-		v3::InboundXcmpStatus::<T>::put(states);
-		return
-	};
+	/// Migrates `QueueConfigData` to v4, removing deprecated fields and bumping page
+	/// thresholds to at least the default values.
+	pub struct UncheckedMigrationToV4<T: Config>(PhantomData<T>);
+
+	impl<T: Config> OnRuntimeUpgrade for UncheckedMigrationToV4<T> {
+		fn on_runtime_upgrade() -> Weight {
+			let translate = |pre: v2::QueueConfigData| -> QueueConfigData {
+				let pre_default = v2::QueueConfigData::default();
+				// If the previous values are the default ones, let's replace them with the new
+				// default.
+				if pre.suspend_threshold == pre_default.suspend_threshold &&
+					pre.drop_threshold == pre_default.drop_threshold &&
+					pre.resume_threshold == pre_default.resume_threshold
+				{
+					return QueueConfigData::default()
+				}
+
+				// If the previous values are not the default ones, let's leave them as they are.
+				QueueConfigData {
+					suspend_threshold: pre.suspend_threshold,
+					drop_threshold: pre.drop_threshold,
+					resume_threshold: pre.resume_threshold,
+				}
+			};
+
+			if QueueConfig::<T>::translate(|pre| pre.map(translate)).is_err() {
+				log::error!(
+					target: crate::LOG_TARGET,
+					"unexpected error when performing translation of the QueueConfig type \
+					during storage upgrade to v4"
+				);
+			}
 
-	// Finally! We have a proper message.
-	T::XcmpQueue::enqueue_message(msg.as_bounded_slice(), next.sender);
-	log::debug!(target: LOG, "Migrated HRMP message to MQ: {:?}", (next.sender, block_number));
-	v3::InboundXcmpStatus::<T>::put(states);
+			T::DbWeight::get().reads_writes(1, 1)
+		}
+	}
+
+	/// [`UncheckedMigrationToV4`] wrapped in a
+	/// [`VersionedMigration`](frame_support::migrations::VersionedMigration), ensuring the
+	/// migration is only performed when on-chain version is 3.
+	pub type MigrationToV4<T> = frame_support::migrations::VersionedMigration<
+		3,
+		4,
+		UncheckedMigrationToV4<T>,
+		Pallet<T>,
+		<T as frame_system::Config>::DbWeight,
+	>;
 }
 
-#[cfg(test)]
+#[cfg(all(feature = "try-runtime", test))]
 mod tests {
 	use super::*;
 	use crate::mock::{new_test_ext, Test};
@@ -230,14 +329,20 @@ mod tests {
 		};
 
 		new_test_ext().execute_with(|| {
+			let storage_version = StorageVersion::new(1);
+			storage_version.put::<Pallet<Test>>();
+
 			frame_support::storage::unhashed::put_raw(
 				&crate::QueueConfig::<Test>::hashed_key(),
 				&v1.encode(),
 			);
 
-			migrate_to_v2::<Test>();
+			let bytes = v2::MigrationToV2::<Test>::pre_upgrade();
+			assert!(bytes.is_ok());
+			v2::MigrationToV2::<Test>::on_runtime_upgrade();
+			assert!(v2::MigrationToV2::<Test>::post_upgrade(bytes.unwrap()).is_ok());
 
-			let v2 = crate::QueueConfig::<Test>::get();
+			let v2 = v2::QueueConfig::<Test>::get();
 
 			assert_eq!(v1.suspend_threshold, v2.suspend_threshold);
 			assert_eq!(v1.drop_threshold, v2.drop_threshold);
@@ -247,4 +352,70 @@ mod tests {
 			assert_eq!(v1.xcmp_max_individual_weight, v2.xcmp_max_individual_weight.ref_time());
 		});
 	}
+
+	#[test]
+	#[allow(deprecated)]
+	fn test_migration_to_v4() {
+		new_test_ext().execute_with(|| {
+			let storage_version = StorageVersion::new(3);
+			storage_version.put::<Pallet<Test>>();
+
+			let v2 = v2::QueueConfigData {
+				drop_threshold: 5,
+				suspend_threshold: 2,
+				resume_threshold: 1,
+				..Default::default()
+			};
+
+			frame_support::storage::unhashed::put_raw(
+				&crate::QueueConfig::<Test>::hashed_key(),
+				&v2.encode(),
+			);
+
+			let bytes = v4::MigrationToV4::<Test>::pre_upgrade();
+			assert!(bytes.is_ok());
+			v4::MigrationToV4::<Test>::on_runtime_upgrade();
+			assert!(v4::MigrationToV4::<Test>::post_upgrade(bytes.unwrap()).is_ok());
+
+			let v4 = QueueConfig::<Test>::get();
+
+			assert_eq!(
+				v4,
+				QueueConfigData { suspend_threshold: 32, drop_threshold: 48, resume_threshold: 8 }
+			);
+		});
+
+		new_test_ext().execute_with(|| {
+			let storage_version = StorageVersion::new(3);
+			storage_version.put::<Pallet<Test>>();
+
+			let v2 = v2::QueueConfigData {
+				drop_threshold: 100,
+				suspend_threshold: 50,
+				resume_threshold: 40,
+				..Default::default()
+			};
+
+			frame_support::storage::unhashed::put_raw(
+				&crate::QueueConfig::<Test>::hashed_key(),
+				&v2.encode(),
+			);
+
+			let bytes = v4::MigrationToV4::<Test>::pre_upgrade();
+			assert!(bytes.is_ok());
+			v4::MigrationToV4::<Test>::on_runtime_upgrade();
+			assert!(v4::MigrationToV4::<Test>::post_upgrade(bytes.unwrap()).is_ok());
+
+			let v4 = QueueConfig::<Test>::get();
+
+			assert_eq!(
+				v4,
+				QueueConfigData {
+					suspend_threshold: 50,
+					drop_threshold: 100,
+					resume_threshold: 40
+				}
+			);
+		});
+	}
 }
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs
index c6c9735ecb1..6b1948e7125 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs
@@ -954,8 +954,12 @@ pub type SignedExtra = (
 pub type UncheckedExtrinsic =
 	generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>;
 /// Migrations to apply on runtime upgrade.
-pub type Migrations =
-	(pallet_collator_selection::migration::v1::MigrateToV1<Runtime>, InitStorageVersions);
+pub type Migrations = (
+	pallet_collator_selection::migration::v1::MigrateToV1<Runtime>,
+	InitStorageVersions,
+	// unreleased
+	cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4<Runtime>,
+);
 
 /// Migration to initialize storage versions for pallets added after genesis.
 ///
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
index 4eb3e2471ce..bd97c1c2ca5 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
@@ -941,6 +941,8 @@ pub type Migrations = (
 	InitStorageVersions,
 	// unreleased
 	DeleteUndecodableStorage,
+	// unreleased
+	cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4<Runtime>,
 );
 
 /// Asset Hub Westend has some undecodable storage, delete it.
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs
index 152e071a26a..2fcc7121c0c 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs
@@ -118,6 +118,7 @@ pub type Migrations = (
 	pallet_collator_selection::migration::v1::MigrateToV1<Runtime>,
 	pallet_multisig::migrations::v1::MigrateToV1<Runtime>,
 	InitStorageVersions,
+	cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4<Runtime>,
 );
 
 /// Migration to initialize storage versions for pallets added after genesis.
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs
index 7b8ea1b7734..121aa5f0e86 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs
@@ -118,6 +118,8 @@ pub type Migrations = (
 	pallet_collator_selection::migration::v1::MigrateToV1<Runtime>,
 	pallet_multisig::migrations::v1::MigrateToV1<Runtime>,
 	InitStorageVersions,
+	// unreleased
+	cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4<Runtime>,
 );
 
 /// Migration to initialize storage versions for pallets added after genesis.
diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs
index 1017ac30499..be82cf32fbb 100644
--- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs
@@ -707,6 +707,8 @@ pub type UncheckedExtrinsic =
 type Migrations = (
 	// unreleased
 	pallet_collator_selection::migration::v1::MigrateToV1<Runtime>,
+	// unreleased
+	cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4<Runtime>,
 );
 
 /// Executive: handles dispatch to the various modules.
diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs
index 77bc93fb968..33c2e26573e 100644
--- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs
+++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs
@@ -100,9 +100,11 @@ pub type UncheckedExtrinsic =
 /// Migrations to apply on runtime upgrade.
 pub type Migrations = (
 	cumulus_pallet_parachain_system::migration::Migration<Runtime>,
-	cumulus_pallet_xcmp_queue::migration::MigrationToV3<Runtime>,
+	cumulus_pallet_xcmp_queue::migration::v2::MigrationToV2<Runtime>,
+	cumulus_pallet_xcmp_queue::migration::v3::MigrationToV3<Runtime>,
 	pallet_contracts::Migration<Runtime>,
 	// unreleased
+	cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4<Runtime>,
 );
 
 type EventRecord = frame_system::EventRecord<
diff --git a/prdoc/pr_2142.prdoc b/prdoc/pr_2142.prdoc
new file mode 100644
index 00000000000..ae2ac3db9dd
--- /dev/null
+++ b/prdoc/pr_2142.prdoc
@@ -0,0 +1,14 @@
+title: Cleanup XCMP `QueueConfigData`
+
+doc:
+  - audience: Parachain Dev
+    description: Removes obsolete fields from the `QueueConfigData` structure. For the remaining fields, if they use the old defaults, we replace them with the new defaults.
+
+migrations:
+  runtime:
+    - pallet: "cumulus_pallet_xcmp_queue"
+      description: "v4: Removes obsolete fields from the `QueueConfigData` structure. For the remaining fields, if they use the old defaults, we replace them with the new defaults."
+
+crates: []
+
+host_functions: []
-- 
GitLab