1use crate::{
18 pallet::CurrentMigration, Config, CurrentXcmVersion, Pallet, VersionMigrationStage,
19 VersionNotifyTargets,
20};
21use frame_support::{
22 pallet_prelude::*,
23 traits::{OnRuntimeUpgrade, StorageVersion, UncheckedOnRuntimeUpgrade},
24 weights::Weight,
25};
26
27const DEFAULT_PROOF_SIZE: u64 = 64 * 1024;
28
29pub mod data {
31 use crate::*;
32
33 pub(crate) trait NeedsMigration {
35 type MigratedData;
36
37 fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool;
39
40 fn try_migrate(self, to_xcm_version: XcmVersion) -> Result<Option<Self::MigratedData>, ()>;
43 }
44
45 impl<B, M> NeedsMigration for BoundedVec<(B, VersionedLocation), M> {
47 type MigratedData = Self;
48
49 fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool {
50 self.iter()
51 .any(|(_, unlocker)| unlocker.identify_version() < minimal_allowed_xcm_version)
52 }
53
54 fn try_migrate(
55 mut self,
56 to_xcm_version: XcmVersion,
57 ) -> Result<Option<Self::MigratedData>, ()> {
58 let mut was_modified = false;
59 for locked in self.iter_mut() {
60 if locked.1.identify_version() < to_xcm_version {
61 let Ok(new_unlocker) = locked.1.clone().into_version(to_xcm_version) else {
62 return Err(())
63 };
64 locked.1 = new_unlocker;
65 was_modified = true;
66 }
67 }
68
69 if was_modified {
70 Ok(Some(self))
71 } else {
72 Ok(None)
73 }
74 }
75 }
76
77 impl<BlockNumber> NeedsMigration for QueryStatus<BlockNumber> {
79 type MigratedData = Self;
80
81 fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool {
82 match &self {
83 QueryStatus::Pending { responder, maybe_match_querier, .. } =>
84 responder.identify_version() < minimal_allowed_xcm_version ||
85 maybe_match_querier
86 .as_ref()
87 .map(|v| v.identify_version() < minimal_allowed_xcm_version)
88 .unwrap_or(false),
89 QueryStatus::VersionNotifier { origin, .. } =>
90 origin.identify_version() < minimal_allowed_xcm_version,
91 QueryStatus::Ready { response, .. } =>
92 response.identify_version() < minimal_allowed_xcm_version,
93 }
94 }
95
96 fn try_migrate(self, to_xcm_version: XcmVersion) -> Result<Option<Self::MigratedData>, ()> {
97 if !self.needs_migration(to_xcm_version) {
98 return Ok(None)
99 }
100
101 match self {
103 QueryStatus::Pending { responder, maybe_match_querier, maybe_notify, timeout } => {
104 let Ok(responder) = responder.into_version(to_xcm_version) else {
105 return Err(())
106 };
107 let Ok(maybe_match_querier) =
108 maybe_match_querier.map(|mmq| mmq.into_version(to_xcm_version)).transpose()
109 else {
110 return Err(())
111 };
112 Ok(Some(QueryStatus::Pending {
113 responder,
114 maybe_match_querier,
115 maybe_notify,
116 timeout,
117 }))
118 },
119 QueryStatus::VersionNotifier { origin, is_active } => origin
120 .into_version(to_xcm_version)
121 .map(|origin| Some(QueryStatus::VersionNotifier { origin, is_active })),
122 QueryStatus::Ready { response, at } => response
123 .into_version(to_xcm_version)
124 .map(|response| Some(QueryStatus::Ready { response, at })),
125 }
126 }
127 }
128
129 impl<A> NeedsMigration for (XcmVersion, A, VersionedAssetId) {
131 type MigratedData = Self;
132
133 fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool {
134 self.0 < minimal_allowed_xcm_version ||
135 self.2.identify_version() < minimal_allowed_xcm_version
136 }
137
138 fn try_migrate(self, to_xcm_version: XcmVersion) -> Result<Option<Self::MigratedData>, ()> {
139 if !self.needs_migration(to_xcm_version) {
140 return Ok(None)
141 }
142
143 let Ok(asset_id) = self.2.into_version(to_xcm_version) else { return Err(()) };
144 Ok(Some((to_xcm_version, self.1, asset_id)))
145 }
146 }
147
148 impl<ConsumerIdentifier, MaxConsumers: Get<u32>> NeedsMigration
150 for RemoteLockedFungibleRecord<ConsumerIdentifier, MaxConsumers>
151 {
152 type MigratedData = Self;
153
154 fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool {
155 self.owner.identify_version() < minimal_allowed_xcm_version ||
156 self.locker.identify_version() < minimal_allowed_xcm_version
157 }
158
159 fn try_migrate(self, to_xcm_version: XcmVersion) -> Result<Option<Self::MigratedData>, ()> {
160 if !self.needs_migration(to_xcm_version) {
161 return Ok(None)
162 }
163
164 let RemoteLockedFungibleRecord { amount, owner, locker, consumers } = self;
165
166 let Ok(owner) = owner.into_version(to_xcm_version) else { return Err(()) };
167 let Ok(locker) = locker.into_version(to_xcm_version) else { return Err(()) };
168
169 Ok(Some(RemoteLockedFungibleRecord { amount, owner, locker, consumers }))
170 }
171 }
172
173 impl<M: Get<u32>, T: Config> NeedsMigration
175 for (&VersionedLocation, AuthorizedAliasesEntry<TicketOf<T>, M>, PhantomData<T>)
176 {
177 type MigratedData = (VersionedLocation, AuthorizedAliasesEntry<TicketOf<T>, M>);
178
179 fn needs_migration(&self, required_version: XcmVersion) -> bool {
180 self.0.identify_version() != required_version ||
181 self.1
182 .aliasers
183 .iter()
184 .any(|alias| alias.location.identify_version() != required_version)
185 }
186
187 fn try_migrate(
188 self,
189 required_version: XcmVersion,
190 ) -> Result<Option<Self::MigratedData>, ()> {
191 if !self.needs_migration(required_version) {
192 return Ok(None)
193 }
194
195 let key = if self.0.identify_version() != required_version {
196 let Ok(converted_key) = self.0.clone().into_version(required_version) else {
197 return Err(())
198 };
199 converted_key
200 } else {
201 self.0.clone()
202 };
203
204 let mut new_aliases = BoundedVec::<OriginAliaser, M>::new();
205 let (aliasers, ticket) = (self.1.aliasers, self.1.ticket);
206 for alias in aliasers {
207 let OriginAliaser { mut location, expiry } = alias.clone();
208 if location.identify_version() != required_version {
209 location = location.into_version(required_version)?;
210 }
211 new_aliases.try_push(OriginAliaser { location, expiry }).map_err(|_| ())?;
212 }
213
214 Ok(Some((key, AuthorizedAliasesEntry { aliasers: new_aliases, ticket })))
215 }
216 }
217
218 impl<T: Config> Pallet<T> {
219 pub(crate) fn migrate_data_to_xcm_version(
221 weight: &mut Weight,
222 required_xcm_version: XcmVersion,
223 ) {
224 const LOG_TARGET: &str = "runtime::xcm::pallet_xcm::migrate_data_to_xcm_version";
225
226 let queries_to_migrate = Queries::<T>::iter().filter_map(|(id, data)| {
228 weight.saturating_add(T::DbWeight::get().reads(1));
229 match data.try_migrate(required_xcm_version) {
230 Ok(Some(new_data)) => Some((id, new_data)),
231 Ok(None) => None,
232 Err(_) => {
233 tracing::error!(
234 target: LOG_TARGET,
235 ?id,
236 ?required_xcm_version,
237 "`Queries` cannot be migrated!"
238 );
239 None
240 },
241 }
242 });
243 for (id, new_data) in queries_to_migrate {
244 tracing::info!(
245 target: LOG_TARGET,
246 query_id = ?id,
247 ?new_data,
248 "Migrating `Queries`"
249 );
250 Queries::<T>::insert(id, new_data);
251 weight.saturating_add(T::DbWeight::get().writes(1));
252 }
253
254 let locked_fungibles_to_migrate =
256 LockedFungibles::<T>::iter().filter_map(|(id, data)| {
257 weight.saturating_add(T::DbWeight::get().reads(1));
258 match data.try_migrate(required_xcm_version) {
259 Ok(Some(new_data)) => Some((id, new_data)),
260 Ok(None) => None,
261 Err(_) => {
262 tracing::error!(
263 target: LOG_TARGET,
264 ?id,
265 ?required_xcm_version,
266 "`LockedFungibles` cannot be migrated!"
267 );
268 None
269 },
270 }
271 });
272 for (id, new_data) in locked_fungibles_to_migrate {
273 tracing::info!(
274 target: LOG_TARGET,
275 account_id = ?id,
276 ?new_data,
277 "Migrating `LockedFungibles`"
278 );
279 LockedFungibles::<T>::insert(id, new_data);
280 weight.saturating_add(T::DbWeight::get().writes(1));
281 }
282
283 let remote_locked_fungibles_to_migrate =
285 RemoteLockedFungibles::<T>::iter().filter_map(|(id, data)| {
286 weight.saturating_add(T::DbWeight::get().reads(1));
287 match data.try_migrate(required_xcm_version) {
288 Ok(Some(new_data)) => Some((id, new_data)),
289 Ok(None) => None,
290 Err(_) => {
291 tracing::error!(
292 target: LOG_TARGET,
293 ?id,
294 ?required_xcm_version,
295 "`RemoteLockedFungibles` data cannot be migrated!"
296 );
297 None
298 },
299 }
300 });
301 for (id, new_data) in remote_locked_fungibles_to_migrate {
302 tracing::info!(
303 target: LOG_TARGET,
304 key = ?id,
305 amount = ?new_data.amount,
306 locker = ?new_data.locker,
307 owner = ?new_data.owner,
308 consumers_count = ?new_data.consumers.len(),
309 "Migrating `RemoteLockedFungibles` data"
310 );
311 RemoteLockedFungibles::<T>::insert(id, new_data);
312 weight.saturating_add(T::DbWeight::get().writes(1));
313 }
314
315 let remote_locked_fungibles_keys_to_migrate = RemoteLockedFungibles::<T>::iter_keys()
317 .filter_map(|key| {
318 if key.needs_migration(required_xcm_version) {
319 let old_key = key.clone();
320 match key.try_migrate(required_xcm_version) {
321 Ok(Some(new_key)) => Some((old_key, new_key)),
322 Ok(None) => None,
323 Err(_) => {
324 tracing::error!(
325 target: LOG_TARGET,
326 id = ?old_key,
327 ?required_xcm_version,
328 "`RemoteLockedFungibles` key cannot be migrated!"
329 );
330 None
331 },
332 }
333 } else {
334 None
335 }
336 });
337 for (old_key, new_key) in remote_locked_fungibles_keys_to_migrate {
338 weight.saturating_add(T::DbWeight::get().reads(1));
339 if RemoteLockedFungibles::<T>::get(&new_key).is_some() {
341 tracing::error!(
342 target: LOG_TARGET,
343 ?old_key,
344 ?new_key,
345 "`RemoteLockedFungibles` already contains data for a `new_key`!"
346 );
347 continue;
350 }
351
352 tracing::info!(
353 target: LOG_TARGET,
354 ?old_key,
355 ?new_key,
356 "Migrating `RemoteLockedFungibles` key"
357 );
358
359 RemoteLockedFungibles::<T>::swap::<
361 (
362 NMapKey<Twox64Concat, XcmVersion>,
363 NMapKey<Blake2_128Concat, T::AccountId>,
364 NMapKey<Blake2_128Concat, VersionedAssetId>,
365 ),
366 _,
367 _,
368 >(&old_key, &new_key);
369 weight.saturating_add(T::DbWeight::get().writes(1));
370 }
371
372 let aliases_to_migrate = AuthorizedAliases::<T>::iter().filter_map(|(id, data)| {
374 weight.saturating_add(T::DbWeight::get().reads(1));
375 match (&id, data, PhantomData::<T>).try_migrate(required_xcm_version) {
376 Ok(Some((new_id, new_data))) => Some((id, new_id, new_data)),
377 Ok(None) => None,
378 Err(_) => {
379 tracing::error!(
380 target: LOG_TARGET,
381 ?id,
382 ?required_xcm_version,
383 "`AuthorizedAliases` cannot be migrated!"
384 );
385 None
386 },
387 }
388 });
389 let mut count = 0;
390 for (old_id, new_id, new_data) in aliases_to_migrate {
391 tracing::info!(
392 target: LOG_TARGET,
393 ?new_id,
394 ?new_data,
395 "Migrating `AuthorizedAliases`"
396 );
397 AuthorizedAliases::<T>::remove(old_id);
398 AuthorizedAliases::<T>::insert(new_id, new_data);
399 count = count + 1;
400 }
401 weight.saturating_add(T::DbWeight::get().writes(count * 2));
403 }
404 }
405}
406
407pub mod v1 {
408 use super::*;
409 use crate::{CurrentMigration, VersionMigrationStage};
410
411 pub struct VersionUncheckedMigrateToV1<T>(core::marker::PhantomData<T>);
417 impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateToV1<T> {
418 fn on_runtime_upgrade() -> Weight {
419 let mut weight = T::DbWeight::get().reads(1);
420
421 if StorageVersion::get::<Pallet<T>>() != 0 {
422 tracing::warn!("skipping v1, should be removed");
423 return weight
424 }
425
426 weight.saturating_accrue(T::DbWeight::get().writes(1));
427 CurrentMigration::<T>::put(VersionMigrationStage::default());
428
429 let translate = |pre: (u64, u64, u32)| -> Option<(u64, Weight, u32)> {
430 weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
431 let translated = (pre.0, Weight::from_parts(pre.1, DEFAULT_PROOF_SIZE), pre.2);
432 tracing::info!("Migrated VersionNotifyTarget {:?} to {:?}", pre, translated);
433 Some(translated)
434 };
435
436 VersionNotifyTargets::<T>::translate_values(translate);
437
438 tracing::info!("v1 applied successfully");
439 weight.saturating_accrue(T::DbWeight::get().writes(1));
440 StorageVersion::new(1).put::<Pallet<T>>();
441 weight
442 }
443 }
444
445 pub type MigrateToV1<T> = frame_support::migrations::VersionedMigration<
450 0,
451 1,
452 VersionUncheckedMigrateToV1<T>,
453 crate::pallet::Pallet<T>,
454 <T as frame_system::Config>::DbWeight,
455 >;
456}
457
458pub struct MigrateToLatestXcmVersion<T>(core::marker::PhantomData<T>);
464impl<T: Config> OnRuntimeUpgrade for MigrateToLatestXcmVersion<T> {
465 fn on_runtime_upgrade() -> Weight {
466 let mut weight = T::DbWeight::get().reads(1);
467
468 CurrentMigration::<T>::put(VersionMigrationStage::default());
470 weight.saturating_accrue(T::DbWeight::get().writes(1));
471
472 let latest = CurrentXcmVersion::get();
474 Pallet::<T>::migrate_data_to_xcm_version(&mut weight, latest);
475
476 weight
477 }
478
479 #[cfg(feature = "try-runtime")]
480 fn post_upgrade(_: alloc::vec::Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
481 use data::NeedsMigration;
482 const LOG_TARGET: &str = "runtime::xcm::pallet_xcm::migrate_to_latest";
483
484 let latest = CurrentXcmVersion::get();
485
486 let number_of_queries_to_migrate = crate::Queries::<T>::iter()
487 .filter(|(id, data)| {
488 let needs_migration = data.needs_migration(latest);
489 if needs_migration {
490 tracing::warn!(
491 target: LOG_TARGET,
492 query_id = ?id,
493 query = ?data,
494 "Query was not migrated!"
495 )
496 }
497 needs_migration
498 })
499 .count();
500
501 let number_of_locked_fungibles_to_migrate = crate::LockedFungibles::<T>::iter()
502 .filter_map(|(id, data)| {
503 if data.needs_migration(latest) {
504 tracing::warn!(
505 target: LOG_TARGET,
506 ?id,
507 ?data,
508 "LockedFungibles item was not migrated!"
509 );
510 Some(true)
511 } else {
512 None
513 }
514 })
515 .count();
516
517 let number_of_remote_locked_fungibles_to_migrate =
518 crate::RemoteLockedFungibles::<T>::iter()
519 .filter_map(|(key, data)| {
520 if key.needs_migration(latest) || data.needs_migration(latest) {
521 tracing::warn!(
522 target: LOG_TARGET,
523 ?key,
524 "RemoteLockedFungibles item was not migrated!"
525 );
526 Some(true)
527 } else {
528 None
529 }
530 })
531 .count();
532
533 ensure!(number_of_queries_to_migrate == 0, "must migrate all `Queries`.");
534 ensure!(number_of_locked_fungibles_to_migrate == 0, "must migrate all `LockedFungibles`.");
535 ensure!(
536 number_of_remote_locked_fungibles_to_migrate == 0,
537 "must migrate all `RemoteLockedFungibles`."
538 );
539
540 Ok(())
541 }
542}