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