1pub mod v5;
20pub mod v6;
21pub mod v7;
22
23use crate::{Config, OverweightIndex, Pallet, QueueConfig, QueueConfigData, DEFAULT_POV_SIZE};
24use alloc::vec::Vec;
25use cumulus_primitives_core::XcmpMessageFormat;
26use frame_support::{
27 pallet_prelude::*,
28 traits::{EnqueueMessage, StorageVersion, UncheckedOnRuntimeUpgrade},
29 weights::{constants::WEIGHT_REF_TIME_PER_MILLIS, Weight},
30};
31
32pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(7);
34
35pub const LOG: &str = "runtime::xcmp-queue-migration";
36
37mod v1 {
38 use super::*;
39 use codec::{Decode, Encode};
40
41 #[frame_support::storage_alias]
42 pub(crate) type QueueConfig<T: Config> = StorageValue<Pallet<T>, QueueConfigData, ValueQuery>;
43
44 #[derive(Encode, Decode, Debug)]
45 pub struct QueueConfigData {
46 pub suspend_threshold: u32,
47 pub drop_threshold: u32,
48 pub resume_threshold: u32,
49 pub threshold_weight: u64,
50 pub weight_restrict_decay: u64,
51 pub xcmp_max_individual_weight: u64,
52 }
53
54 impl Default for QueueConfigData {
55 fn default() -> Self {
56 QueueConfigData {
57 suspend_threshold: 2,
58 drop_threshold: 5,
59 resume_threshold: 1,
60 threshold_weight: 100_000,
61 weight_restrict_decay: 2,
62 xcmp_max_individual_weight: 20u64 * WEIGHT_REF_TIME_PER_MILLIS,
63 }
64 }
65 }
66}
67
68pub mod v2 {
69 use super::*;
70
71 #[frame_support::storage_alias]
72 pub(crate) type QueueConfig<T: Config> = StorageValue<Pallet<T>, QueueConfigData, ValueQuery>;
73
74 #[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
75 pub struct QueueConfigData {
76 pub suspend_threshold: u32,
77 pub drop_threshold: u32,
78 pub resume_threshold: u32,
79 pub threshold_weight: Weight,
80 pub weight_restrict_decay: Weight,
81 pub xcmp_max_individual_weight: Weight,
82 }
83
84 impl Default for QueueConfigData {
85 fn default() -> Self {
86 Self {
87 suspend_threshold: 2,
88 drop_threshold: 5,
89 resume_threshold: 1,
90 threshold_weight: Weight::from_parts(100_000, 0),
91 weight_restrict_decay: Weight::from_parts(2, 0),
92 xcmp_max_individual_weight: Weight::from_parts(
93 20u64 * WEIGHT_REF_TIME_PER_MILLIS,
94 DEFAULT_POV_SIZE,
95 ),
96 }
97 }
98 }
99
100 pub struct UncheckedMigrationToV2<T: Config>(PhantomData<T>);
103
104 impl<T: Config> UncheckedOnRuntimeUpgrade for UncheckedMigrationToV2<T> {
105 #[allow(deprecated)]
106 fn on_runtime_upgrade() -> Weight {
107 let translate = |pre: v1::QueueConfigData| -> v2::QueueConfigData {
108 v2::QueueConfigData {
109 suspend_threshold: pre.suspend_threshold,
110 drop_threshold: pre.drop_threshold,
111 resume_threshold: pre.resume_threshold,
112 threshold_weight: Weight::from_parts(pre.threshold_weight, 0),
113 weight_restrict_decay: Weight::from_parts(pre.weight_restrict_decay, 0),
114 xcmp_max_individual_weight: Weight::from_parts(
115 pre.xcmp_max_individual_weight,
116 DEFAULT_POV_SIZE,
117 ),
118 }
119 };
120
121 if v2::QueueConfig::<T>::translate(|pre| pre.map(translate)).is_err() {
122 tracing::error!(
123 target: crate::LOG_TARGET,
124 "unexpected error when performing translation of the QueueConfig type \
125 during storage upgrade to v2"
126 );
127 }
128
129 T::DbWeight::get().reads_writes(1, 1)
130 }
131 }
132
133 #[allow(dead_code)]
137 pub type MigrationToV2<T> = frame_support::migrations::VersionedMigration<
138 1,
139 2,
140 UncheckedMigrationToV2<T>,
141 Pallet<T>,
142 <T as frame_system::Config>::DbWeight,
143 >;
144}
145
146pub mod v3 {
147 use super::*;
148 use crate::*;
149
150 #[frame_support::storage_alias]
152 pub(crate) type InboundXcmpStatus<T: Config> =
153 StorageValue<Pallet<T>, Vec<InboundChannelDetails>, OptionQuery>;
154
155 #[frame_support::storage_alias]
157 pub(crate) type InboundXcmpMessages<T: Config> = StorageDoubleMap<
158 Pallet<T>,
159 Blake2_128Concat,
160 ParaId,
161 Twox64Concat,
162 RelayBlockNumber,
163 Vec<u8>,
164 OptionQuery,
165 >;
166
167 #[frame_support::storage_alias]
168 pub(crate) type QueueConfig<T: Config> =
169 StorageValue<Pallet<T>, v2::QueueConfigData, ValueQuery>;
170
171 #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
172 pub struct InboundChannelDetails {
173 pub sender: ParaId,
175 pub state: InboundState,
177 pub message_metadata: Vec<(RelayBlockNumber, XcmpMessageFormat)>,
182 }
183
184 #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)]
185 pub enum InboundState {
186 Ok,
187 Suspended,
188 }
189
190 pub struct UncheckedMigrationToV3<T: Config>(PhantomData<T>);
192
193 impl<T: Config> UncheckedOnRuntimeUpgrade for UncheckedMigrationToV3<T> {
194 fn on_runtime_upgrade() -> Weight {
195 #[frame_support::storage_alias]
196 type Overweight<T: Config> =
197 CountedStorageMap<Pallet<T>, Twox64Concat, OverweightIndex, ParaId>;
198 let overweight_messages = Overweight::<T>::initialize_counter() as u64;
199
200 T::DbWeight::get().reads_writes(overweight_messages, 1)
201 }
202 }
203
204 pub type MigrationToV3<T> = frame_support::migrations::VersionedMigration<
208 2,
209 3,
210 UncheckedMigrationToV3<T>,
211 Pallet<T>,
212 <T as frame_system::Config>::DbWeight,
213 >;
214
215 pub fn lazy_migrate_inbound_queue<T: Config>() {
216 let Some(mut states) = v3::InboundXcmpStatus::<T>::get() else {
217 tracing::debug!(target: LOG, "Lazy migration finished: item gone");
218 return;
219 };
220 let Some(ref mut next) = states.first_mut() else {
221 tracing::debug!(target: LOG, "Lazy migration finished: item empty");
222 v3::InboundXcmpStatus::<T>::kill();
223 return;
224 };
225 tracing::debug!(
226 target: LOG,
227 sibling=?next.sender,
228 msgs_left=%next.message_metadata.len(),
229 "Migrating inbound HRMP channel."
230 );
231 let Some((block_number, format)) = next.message_metadata.pop() else {
233 states.remove(0);
234 v3::InboundXcmpStatus::<T>::put(states);
235 return;
236 };
237 if format != XcmpMessageFormat::ConcatenatedVersionedXcm {
238 tracing::warn!(
239 target: LOG,
240 ?format,
241 "Dropping message (not ConcatenatedVersionedXcm)"
242 );
243 v3::InboundXcmpMessages::<T>::remove(&next.sender, &block_number);
244 v3::InboundXcmpStatus::<T>::put(states);
245 return;
246 }
247
248 let Some(msg) = v3::InboundXcmpMessages::<T>::take(&next.sender, &block_number) else {
249 defensive!("Storage corrupted: HRMP message missing:", (next.sender, block_number));
250 v3::InboundXcmpStatus::<T>::put(states);
251 return;
252 };
253
254 let Ok(msg): Result<BoundedVec<_, _>, _> = msg.try_into() else {
255 tracing::error!(target: LOG, "Message dropped: too big");
256 v3::InboundXcmpStatus::<T>::put(states);
257 return;
258 };
259
260 T::XcmpQueue::enqueue_message(msg.as_bounded_slice(), next.sender);
262 tracing::debug!(target: LOG, next_sender=?next.sender, ?block_number, "Migrated HRMP message to MQ");
263 v3::InboundXcmpStatus::<T>::put(states);
264 }
265}
266
267pub mod v4 {
268 use super::*;
269
270 pub struct UncheckedMigrationToV4<T: Config>(PhantomData<T>);
273
274 impl<T: Config> UncheckedOnRuntimeUpgrade for UncheckedMigrationToV4<T> {
275 fn on_runtime_upgrade() -> Weight {
276 let translate = |pre: v2::QueueConfigData| -> QueueConfigData {
277 let pre_default = v2::QueueConfigData::default();
278 if pre.suspend_threshold == pre_default.suspend_threshold &&
281 pre.drop_threshold == pre_default.drop_threshold &&
282 pre.resume_threshold == pre_default.resume_threshold
283 {
284 return QueueConfigData::default();
285 }
286
287 QueueConfigData {
289 suspend_threshold: pre.suspend_threshold,
290 drop_threshold: pre.drop_threshold,
291 resume_threshold: pre.resume_threshold,
292 }
293 };
294
295 if QueueConfig::<T>::translate(|pre| pre.map(translate)).is_err() {
296 tracing::error!(
297 target: crate::LOG_TARGET,
298 "unexpected error when performing translation of the QueueConfig type \
299 during storage upgrade to v4"
300 );
301 }
302
303 T::DbWeight::get().reads_writes(1, 1)
304 }
305 }
306
307 pub type MigrationToV4<T> = frame_support::migrations::VersionedMigration<
311 3,
312 4,
313 UncheckedMigrationToV4<T>,
314 Pallet<T>,
315 <T as frame_system::Config>::DbWeight,
316 >;
317}
318
319#[cfg(all(feature = "try-runtime", test))]
320mod tests {
321 use super::*;
322 use crate::mock::{new_test_ext, Test};
323 use frame_support::traits::OnRuntimeUpgrade;
324
325 #[test]
326 #[allow(deprecated)]
327 fn test_migration_to_v2() {
328 let v1 = v1::QueueConfigData {
329 suspend_threshold: 5,
330 drop_threshold: 12,
331 resume_threshold: 3,
332 threshold_weight: 333_333,
333 weight_restrict_decay: 1,
334 xcmp_max_individual_weight: 10_000_000_000,
335 };
336
337 new_test_ext().execute_with(|| {
338 let storage_version = StorageVersion::new(1);
339 storage_version.put::<Pallet<Test>>();
340
341 frame_support::storage::unhashed::put_raw(
342 &crate::QueueConfig::<Test>::hashed_key(),
343 &v1.encode(),
344 );
345
346 let bytes = v2::MigrationToV2::<Test>::pre_upgrade();
347 assert!(bytes.is_ok());
348 v2::MigrationToV2::<Test>::on_runtime_upgrade();
349 assert!(v2::MigrationToV2::<Test>::post_upgrade(bytes.unwrap()).is_ok());
350
351 let v2 = v2::QueueConfig::<Test>::get();
352
353 assert_eq!(v1.suspend_threshold, v2.suspend_threshold);
354 assert_eq!(v1.drop_threshold, v2.drop_threshold);
355 assert_eq!(v1.resume_threshold, v2.resume_threshold);
356 assert_eq!(v1.threshold_weight, v2.threshold_weight.ref_time());
357 assert_eq!(v1.weight_restrict_decay, v2.weight_restrict_decay.ref_time());
358 assert_eq!(v1.xcmp_max_individual_weight, v2.xcmp_max_individual_weight.ref_time());
359 });
360 }
361
362 #[test]
363 #[allow(deprecated)]
364 fn test_migration_to_v4() {
365 new_test_ext().execute_with(|| {
366 let storage_version = StorageVersion::new(3);
367 storage_version.put::<Pallet<Test>>();
368
369 let v2 = v2::QueueConfigData {
370 drop_threshold: 5,
371 suspend_threshold: 2,
372 resume_threshold: 1,
373 ..Default::default()
374 };
375
376 frame_support::storage::unhashed::put_raw(
377 &crate::QueueConfig::<Test>::hashed_key(),
378 &v2.encode(),
379 );
380
381 let bytes = v4::MigrationToV4::<Test>::pre_upgrade();
382 assert!(bytes.is_ok());
383 v4::MigrationToV4::<Test>::on_runtime_upgrade();
384 assert!(v4::MigrationToV4::<Test>::post_upgrade(bytes.unwrap()).is_ok());
385
386 let v4 = QueueConfig::<Test>::get();
387
388 assert_eq!(
389 v4,
390 QueueConfigData { suspend_threshold: 32, drop_threshold: 48, resume_threshold: 8 }
391 );
392 });
393
394 new_test_ext().execute_with(|| {
395 let storage_version = StorageVersion::new(3);
396 storage_version.put::<Pallet<Test>>();
397
398 let v2 = v2::QueueConfigData {
399 drop_threshold: 100,
400 suspend_threshold: 50,
401 resume_threshold: 40,
402 ..Default::default()
403 };
404
405 frame_support::storage::unhashed::put_raw(
406 &crate::QueueConfig::<Test>::hashed_key(),
407 &v2.encode(),
408 );
409
410 let bytes = v4::MigrationToV4::<Test>::pre_upgrade();
411 assert!(bytes.is_ok());
412 v4::MigrationToV4::<Test>::on_runtime_upgrade();
413 assert!(v4::MigrationToV4::<Test>::post_upgrade(bytes.unwrap()).is_ok());
414
415 let v4 = QueueConfig::<Test>::get();
416
417 assert_eq!(
418 v4,
419 QueueConfigData {
420 suspend_threshold: 50,
421 drop_threshold: 100,
422 resume_threshold: 40
423 }
424 );
425 });
426 }
427}