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 },
90 QueryStatus::VersionNotifier { origin, .. } => {
91 origin.identify_version() < minimal_allowed_xcm_version
92 },
93 QueryStatus::Ready { response, .. } => {
94 response.identify_version() < minimal_allowed_xcm_version
95 },
96 }
97 }
98
99 fn try_migrate(self, to_xcm_version: XcmVersion) -> Result<Option<Self::MigratedData>, ()> {
100 if !self.needs_migration(to_xcm_version) {
101 return Ok(None);
102 }
103
104 match self {
106 QueryStatus::Pending { responder, maybe_match_querier, maybe_notify, timeout } => {
107 let Ok(responder) = responder.into_version(to_xcm_version) else {
108 return Err(());
109 };
110 let Ok(maybe_match_querier) =
111 maybe_match_querier.map(|mmq| mmq.into_version(to_xcm_version)).transpose()
112 else {
113 return Err(());
114 };
115 Ok(Some(QueryStatus::Pending {
116 responder,
117 maybe_match_querier,
118 maybe_notify,
119 timeout,
120 }))
121 },
122 QueryStatus::VersionNotifier { origin, is_active } => origin
123 .into_version(to_xcm_version)
124 .map(|origin| Some(QueryStatus::VersionNotifier { origin, is_active })),
125 QueryStatus::Ready { response, at } => response
126 .into_version(to_xcm_version)
127 .map(|response| Some(QueryStatus::Ready { response, at })),
128 }
129 }
130 }
131
132 impl<A> NeedsMigration for (XcmVersion, A, VersionedAssetId) {
134 type MigratedData = Self;
135
136 fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool {
137 self.0 < minimal_allowed_xcm_version ||
138 self.2.identify_version() < minimal_allowed_xcm_version
139 }
140
141 fn try_migrate(self, to_xcm_version: XcmVersion) -> Result<Option<Self::MigratedData>, ()> {
142 if !self.needs_migration(to_xcm_version) {
143 return Ok(None);
144 }
145
146 let Ok(asset_id) = self.2.into_version(to_xcm_version) else { return Err(()) };
147 Ok(Some((to_xcm_version, self.1, asset_id)))
148 }
149 }
150
151 impl<ConsumerIdentifier, MaxConsumers: Get<u32>> NeedsMigration
153 for RemoteLockedFungibleRecord<ConsumerIdentifier, MaxConsumers>
154 {
155 type MigratedData = Self;
156
157 fn needs_migration(&self, minimal_allowed_xcm_version: XcmVersion) -> bool {
158 self.owner.identify_version() < minimal_allowed_xcm_version ||
159 self.locker.identify_version() < minimal_allowed_xcm_version
160 }
161
162 fn try_migrate(self, to_xcm_version: XcmVersion) -> Result<Option<Self::MigratedData>, ()> {
163 if !self.needs_migration(to_xcm_version) {
164 return Ok(None);
165 }
166
167 let RemoteLockedFungibleRecord { amount, owner, locker, consumers } = self;
168
169 let Ok(owner) = owner.into_version(to_xcm_version) else { return Err(()) };
170 let Ok(locker) = locker.into_version(to_xcm_version) else { return Err(()) };
171
172 Ok(Some(RemoteLockedFungibleRecord { amount, owner, locker, consumers }))
173 }
174 }
175
176 impl<M: Get<u32>, T: Config> NeedsMigration
178 for (&VersionedLocation, AuthorizedAliasesEntry<TicketOf<T>, M>, PhantomData<T>)
179 {
180 type MigratedData = (VersionedLocation, AuthorizedAliasesEntry<TicketOf<T>, M>);
181
182 fn needs_migration(&self, required_version: XcmVersion) -> bool {
183 self.0.identify_version() != required_version ||
184 self.1
185 .aliasers
186 .iter()
187 .any(|alias| alias.location.identify_version() != required_version)
188 }
189
190 fn try_migrate(
191 self,
192 required_version: XcmVersion,
193 ) -> Result<Option<Self::MigratedData>, ()> {
194 if !self.needs_migration(required_version) {
195 return Ok(None);
196 }
197
198 let key = if self.0.identify_version() != required_version {
199 let Ok(converted_key) = self.0.clone().into_version(required_version) else {
200 return Err(());
201 };
202 converted_key
203 } else {
204 self.0.clone()
205 };
206
207 let mut new_aliases = BoundedVec::<OriginAliaser, M>::new();
208 let (aliasers, ticket) = (self.1.aliasers, self.1.ticket);
209 for alias in aliasers {
210 let OriginAliaser { mut location, expiry } = alias.clone();
211 if location.identify_version() != required_version {
212 location = location.into_version(required_version)?;
213 }
214 new_aliases.try_push(OriginAliaser { location, expiry }).map_err(|_| ())?;
215 }
216
217 Ok(Some((key, AuthorizedAliasesEntry { aliasers: new_aliases, ticket })))
218 }
219 }
220
221 impl<T: Config> Pallet<T> {
222 pub(crate) fn migrate_data_to_xcm_version(
224 weight: &mut Weight,
225 required_xcm_version: XcmVersion,
226 ) {
227 const LOG_TARGET: &str = "runtime::xcm::pallet_xcm::migrate_data_to_xcm_version";
228
229 let queries_to_migrate = Queries::<T>::iter().filter_map(|(id, data)| {
231 match data.try_migrate(required_xcm_version) {
232 Ok(Some(new_data)) => Some((id, new_data)),
233 Ok(None) => None,
234 Err(_) => {
235 tracing::error!(
236 target: LOG_TARGET,
237 ?id,
238 ?required_xcm_version,
239 "`Queries` cannot be migrated!"
240 );
241 None
242 },
243 }
244 });
245 for (id, new_data) in queries_to_migrate {
246 tracing::info!(
247 target: LOG_TARGET,
248 query_id = ?id,
249 ?new_data,
250 "Migrating `Queries`"
251 );
252 Queries::<T>::insert(id, new_data);
253 weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
254 }
255
256 let locked_fungibles_to_migrate =
258 LockedFungibles::<T>::iter().filter_map(|(id, data)| {
259 match data.try_migrate(required_xcm_version) {
260 Ok(Some(new_data)) => Some((id, new_data)),
261 Ok(None) => None,
262 Err(_) => {
263 tracing::error!(
264 target: LOG_TARGET,
265 ?id,
266 ?required_xcm_version,
267 "`LockedFungibles` cannot be migrated!"
268 );
269 None
270 },
271 }
272 });
273 for (id, new_data) in locked_fungibles_to_migrate {
274 tracing::info!(
275 target: LOG_TARGET,
276 account_id = ?id,
277 ?new_data,
278 "Migrating `LockedFungibles`"
279 );
280 LockedFungibles::<T>::insert(id, new_data);
281 weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
282 }
283
284 let remote_locked_fungibles_to_migrate =
286 RemoteLockedFungibles::<T>::iter().filter_map(|(id, data)| {
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_accrue(T::DbWeight::get().reads_writes(1, 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_accrue(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_accrue(T::DbWeight::get().writes(1));
370 }
371
372 let aliases_to_migrate = AuthorizedAliases::<T>::iter().filter_map(|(id, data)| {
374 weight.saturating_accrue(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_accrue(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}