pallet_contracts_for_drink/
migration.rs1pub mod v09;
61pub mod v10;
62pub mod v11;
63pub mod v12;
64pub mod v13;
65pub mod v14;
66pub mod v15;
67include!(concat!(env!("OUT_DIR"), "/migration_codegen.rs"));
68
69use crate::{weights::WeightInfo, Config, Error, MigrationInProgress, Pallet, Weight, LOG_TARGET};
70use codec::{Codec, Decode};
71use frame_support::{
72 pallet_prelude::*,
73 traits::{ConstU32, OnRuntimeUpgrade},
74};
75use sp_runtime::Saturating;
76use sp_std::marker::PhantomData;
77
78#[cfg(feature = "try-runtime")]
79use sp_std::prelude::*;
80
81#[cfg(feature = "try-runtime")]
82use sp_runtime::TryRuntimeError;
83
84const PROOF_ENCODE: &str = "Tuple::max_encoded_len() < Cursor::max_encoded_len()` is verified in `Self::integrity_test()`; qed";
85const PROOF_DECODE: &str =
86 "We encode to the same type in this trait only. No other code touches this item; qed";
87
88fn invalid_version(version: StorageVersion) -> ! {
89 panic!("Required migration {version:?} not supported by this runtime. This is a bug.");
90}
91
92pub type Cursor = BoundedVec<u8, ConstU32<1024>>;
95
96pub enum IsFinished {
98 Yes,
99 No,
100}
101
102pub trait MigrationStep: Codec + MaxEncodedLen + Default {
107 const VERSION: u16;
109
110 fn max_step_weight() -> Weight;
112
113 fn step(&mut self) -> (IsFinished, Weight);
117
118 fn integrity_test(max_block_weight: Weight) {
121 if Self::max_step_weight().any_gt(max_block_weight) {
122 panic!(
123 "Invalid max_step_weight for Migration {}. Value should be lower than {}",
124 Self::VERSION,
125 max_block_weight
126 );
127 }
128
129 let len = <Self as MaxEncodedLen>::max_encoded_len();
130 let max = Cursor::bound();
131 if len > max {
132 panic!(
133 "Migration {} has size {} which is bigger than the maximum of {}",
134 Self::VERSION,
135 len,
136 max,
137 );
138 }
139 }
140
141 #[cfg(feature = "try-runtime")]
143 fn pre_upgrade_step() -> Result<Vec<u8>, TryRuntimeError> {
144 Ok(Vec::new())
145 }
146
147 #[cfg(feature = "try-runtime")]
149 fn post_upgrade_step(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
150 Ok(())
151 }
152}
153
154#[doc(hidden)]
156#[derive(frame_support::DefaultNoBound, Encode, Decode, MaxEncodedLen)]
157pub struct NoopMigration<const N: u16>;
158
159impl<const N: u16> MigrationStep for NoopMigration<N> {
160 const VERSION: u16 = N;
161 fn max_step_weight() -> Weight {
162 Weight::zero()
163 }
164 fn step(&mut self) -> (IsFinished, Weight) {
165 log::debug!(target: LOG_TARGET, "Noop migration for version {}", N);
166 (IsFinished::Yes, Weight::zero())
167 }
168}
169
170mod private {
171 use crate::migration::MigrationStep;
172 pub trait Sealed {}
173 #[impl_trait_for_tuples::impl_for_tuples(10)]
174 #[tuple_types_custom_trait_bound(MigrationStep)]
175 impl Sealed for Tuple {}
176}
177
178pub trait MigrateSequence: private::Sealed {
183 const VERSION_RANGE: (u16, u16);
198
199 fn new(version: StorageVersion) -> Cursor;
201
202 #[cfg(feature = "try-runtime")]
203 fn pre_upgrade_step(_version: StorageVersion) -> Result<Vec<u8>, TryRuntimeError> {
204 Ok(Vec::new())
205 }
206
207 #[cfg(feature = "try-runtime")]
208 fn post_upgrade_step(_version: StorageVersion, _state: Vec<u8>) -> Result<(), TryRuntimeError> {
209 Ok(())
210 }
211
212 fn steps(version: StorageVersion, cursor: &[u8], weight_left: &mut Weight) -> StepResult;
214
215 fn integrity_test(max_block_weight: Weight);
218
219 fn is_upgrade_supported(in_storage: StorageVersion, target: StorageVersion) -> bool {
223 let (low, high) = Self::VERSION_RANGE;
224 target == high && in_storage + 1 == low
225 }
226}
227
228pub struct Migration<T: Config, const TEST_ALL_STEPS: bool = true>(PhantomData<T>);
234
235#[cfg(feature = "try-runtime")]
236impl<T: Config, const TEST_ALL_STEPS: bool> Migration<T, TEST_ALL_STEPS> {
237 fn run_all_steps() -> Result<(), TryRuntimeError> {
238 let mut weight = Weight::zero();
239 let name = <Pallet<T>>::name();
240 loop {
241 let in_progress_version = <Pallet<T>>::on_chain_storage_version() + 1;
242 let state = T::Migrations::pre_upgrade_step(in_progress_version)?;
243 let (status, w) = Self::migrate(Weight::MAX);
244 weight.saturating_accrue(w);
245 log::info!(
246 target: LOG_TARGET,
247 "{name}: Migration step {:?} weight = {}",
248 in_progress_version,
249 weight
250 );
251 T::Migrations::post_upgrade_step(in_progress_version, state)?;
252 if matches!(status, MigrateResult::Completed) {
253 break
254 }
255 }
256
257 let name = <Pallet<T>>::name();
258 log::info!(target: LOG_TARGET, "{name}: Migration steps weight = {}", weight);
259 Ok(())
260 }
261}
262
263impl<T: Config, const TEST_ALL_STEPS: bool> OnRuntimeUpgrade for Migration<T, TEST_ALL_STEPS> {
264 fn on_runtime_upgrade() -> Weight {
265 let name = <Pallet<T>>::name();
266 let latest_version = <Pallet<T>>::current_storage_version();
267 let storage_version = <Pallet<T>>::on_chain_storage_version();
268
269 if storage_version == latest_version {
270 log::warn!(
271 target: LOG_TARGET,
272 "{name}: No Migration performed storage_version = latest_version = {:?}",
273 &storage_version
274 );
275 return T::WeightInfo::on_runtime_upgrade_noop()
276 }
277
278 if Self::in_progress() {
281 log::warn!(
282 target: LOG_TARGET,
283 "{name}: Migration already in progress {:?}",
284 &storage_version
285 );
286
287 return T::WeightInfo::on_runtime_upgrade_in_progress()
288 }
289
290 log::info!(
291 target: LOG_TARGET,
292 "{name}: Upgrading storage from {storage_version:?} to {latest_version:?}.",
293 );
294
295 let cursor = T::Migrations::new(storage_version + 1);
296 MigrationInProgress::<T>::set(Some(cursor));
297
298 #[cfg(feature = "try-runtime")]
299 if TEST_ALL_STEPS {
300 Self::run_all_steps().unwrap();
301 }
302
303 T::WeightInfo::on_runtime_upgrade()
304 }
305
306 #[cfg(feature = "try-runtime")]
307 fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
308 let storage_version = <Pallet<T>>::on_chain_storage_version();
312 let target_version = <Pallet<T>>::current_storage_version();
313
314 ensure!(
315 storage_version != target_version,
316 "No upgrade: Please remove this migration from your runtime upgrade configuration."
317 );
318
319 log::debug!(
320 target: LOG_TARGET,
321 "Requested migration of {} from {:?}(on-chain storage version) to {:?}(current storage version)",
322 <Pallet<T>>::name(), storage_version, target_version
323 );
324
325 ensure!(
326 T::Migrations::is_upgrade_supported(storage_version, target_version),
327 "Unsupported upgrade: VERSION_RANGE should be (on-chain storage version + 1, current storage version)"
328 );
329 Ok(Default::default())
330 }
331
332 #[cfg(feature = "try-runtime")]
333 fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
334 if !TEST_ALL_STEPS {
335 return Ok(())
336 }
337
338 log::info!(target: LOG_TARGET, "=== POST UPGRADE CHECKS ===");
339
340 if let Some(hash) = crate::CodeInfoOf::<T>::iter_keys().next() {
342 crate::CodeInfoOf::<T>::get(hash).expect("CodeInfo exists for hash; qed");
343 }
344 if let Some(hash) = crate::PristineCode::<T>::iter_keys().next() {
345 crate::PristineCode::<T>::get(hash).expect("PristineCode exists for hash; qed");
346 }
347 if let Some(account_id) = crate::ContractInfoOf::<T>::iter_keys().next() {
348 crate::ContractInfoOf::<T>::get(account_id)
349 .expect("ContractInfo exists for account_id; qed");
350 }
351 if let Some(nonce) = crate::DeletionQueue::<T>::iter_keys().next() {
352 crate::DeletionQueue::<T>::get(nonce).expect("DeletionQueue exists for nonce; qed");
353 }
354
355 Ok(())
356 }
357}
358
359#[derive(Debug, PartialEq)]
361pub enum MigrateResult {
362 NoMigrationPerformed,
364 NoMigrationInProgress,
366 InProgress { steps_done: u32 },
368 Completed,
370}
371
372#[derive(Debug, PartialEq)]
374pub enum StepResult {
375 InProgress { cursor: Cursor, steps_done: u32 },
376 Completed { steps_done: u32 },
377}
378
379impl<T: Config, const TEST_ALL_STEPS: bool> Migration<T, TEST_ALL_STEPS> {
380 pub(crate) fn integrity_test() {
383 let max_weight = <T as frame_system::Config>::BlockWeights::get().max_block;
384 T::Migrations::integrity_test(max_weight)
385 }
386
387 pub(crate) fn migrate(weight_limit: Weight) -> (MigrateResult, Weight) {
390 let name = <Pallet<T>>::name();
391 let mut weight_left = weight_limit;
392
393 if weight_left.checked_reduce(T::WeightInfo::migrate()).is_none() {
394 return (MigrateResult::NoMigrationPerformed, Weight::zero())
395 }
396
397 MigrationInProgress::<T>::mutate_exists(|progress| {
398 let Some(cursor_before) = progress.as_mut() else {
399 return (MigrateResult::NoMigrationInProgress, T::WeightInfo::migration_noop())
400 };
401
402 let storage_version = <Pallet<T>>::on_chain_storage_version();
404 let in_progress_version = storage_version + 1;
405
406 log::info!(
407 target: LOG_TARGET,
408 "{name}: Migrating from {:?} to {:?},",
409 storage_version,
410 in_progress_version,
411 );
412
413 let result = match T::Migrations::steps(
414 in_progress_version,
415 cursor_before.as_ref(),
416 &mut weight_left,
417 ) {
418 StepResult::InProgress { cursor, steps_done } => {
419 *progress = Some(cursor);
420 MigrateResult::InProgress { steps_done }
421 },
422 StepResult::Completed { steps_done } => {
423 in_progress_version.put::<Pallet<T>>();
424 if <Pallet<T>>::current_storage_version() != in_progress_version {
425 log::info!(
426 target: LOG_TARGET,
427 "{name}: Next migration is {:?},",
428 in_progress_version + 1
429 );
430 *progress = Some(T::Migrations::new(in_progress_version + 1));
431 MigrateResult::InProgress { steps_done }
432 } else {
433 log::info!(
434 target: LOG_TARGET,
435 "{name}: All migrations done. At version {:?},",
436 in_progress_version
437 );
438 *progress = None;
439 MigrateResult::Completed
440 }
441 },
442 };
443
444 (result, weight_limit.saturating_sub(weight_left))
445 })
446 }
447
448 pub(crate) fn ensure_migrated() -> DispatchResult {
449 if Self::in_progress() {
450 Err(Error::<T>::MigrationInProgress.into())
451 } else {
452 Ok(())
453 }
454 }
455
456 pub(crate) fn in_progress() -> bool {
457 MigrationInProgress::<T>::exists()
458 }
459}
460
461#[impl_trait_for_tuples::impl_for_tuples(10)]
462#[tuple_types_custom_trait_bound(MigrationStep)]
463impl MigrateSequence for Tuple {
464 const VERSION_RANGE: (u16, u16) = {
465 let mut versions: (u16, u16) = (0, 0);
466 for_tuples!(
467 #(
468 match versions {
469 (0, 0) => {
470 versions = (Tuple::VERSION, Tuple::VERSION);
471 },
472 (min_version, last_version) if Tuple::VERSION == last_version + 1 => {
473 versions = (min_version, Tuple::VERSION);
474 },
475 _ => panic!("Migrations must be ordered by their versions with no gaps.")
476 }
477 )*
478 );
479 versions
480 };
481
482 fn new(version: StorageVersion) -> Cursor {
483 for_tuples!(
484 #(
485 if version == Tuple::VERSION {
486 return Tuple::default().encode().try_into().expect(PROOF_ENCODE)
487 }
488 )*
489 );
490 invalid_version(version)
491 }
492
493 #[cfg(feature = "try-runtime")]
494 fn pre_upgrade_step(version: StorageVersion) -> Result<Vec<u8>, TryRuntimeError> {
496 for_tuples!(
497 #(
498 if version == Tuple::VERSION {
499 return Tuple::pre_upgrade_step()
500 }
501 )*
502 );
503 invalid_version(version)
504 }
505
506 #[cfg(feature = "try-runtime")]
507 fn post_upgrade_step(version: StorageVersion, state: Vec<u8>) -> Result<(), TryRuntimeError> {
509 for_tuples!(
510 #(
511 if version == Tuple::VERSION {
512 return Tuple::post_upgrade_step(state)
513 }
514 )*
515 );
516 invalid_version(version)
517 }
518
519 fn steps(version: StorageVersion, mut cursor: &[u8], weight_left: &mut Weight) -> StepResult {
520 for_tuples!(
521 #(
522 if version == Tuple::VERSION {
523 let mut migration = <Tuple as Decode>::decode(&mut cursor)
524 .expect(PROOF_DECODE);
525 let max_weight = Tuple::max_step_weight();
526 let mut steps_done = 0;
527 while weight_left.all_gt(max_weight) {
528 let (finished, weight) = migration.step();
529 steps_done.saturating_accrue(1);
530 weight_left.saturating_reduce(weight);
531 if matches!(finished, IsFinished::Yes) {
532 return StepResult::Completed{ steps_done }
533 }
534 }
535 return StepResult::InProgress{cursor: migration.encode().try_into().expect(PROOF_ENCODE), steps_done }
536 }
537 )*
538 );
539 invalid_version(version)
540 }
541
542 fn integrity_test(max_block_weight: Weight) {
543 for_tuples!(
544 #(
545 Tuple::integrity_test(max_block_weight);
546 )*
547 );
548 }
549}
550
551#[cfg(test)]
552mod test {
553 use super::*;
554 use crate::{
555 migration::codegen::LATEST_MIGRATION_VERSION,
556 tests::{ExtBuilder, Test},
557 };
558
559 #[derive(Default, Encode, Decode, MaxEncodedLen)]
560 struct MockMigration<const N: u16> {
561 count: u16,
563 }
564
565 impl<const N: u16> MigrationStep for MockMigration<N> {
566 const VERSION: u16 = N;
567 fn max_step_weight() -> Weight {
568 Weight::from_all(1)
569 }
570 fn step(&mut self) -> (IsFinished, Weight) {
571 assert!(self.count != N);
572 self.count += 1;
573 if self.count == N {
574 (IsFinished::Yes, Weight::from_all(1))
575 } else {
576 (IsFinished::No, Weight::from_all(1))
577 }
578 }
579 }
580
581 #[test]
582 fn test_storage_version_matches_last_migration_file() {
583 assert_eq!(StorageVersion::new(LATEST_MIGRATION_VERSION), crate::pallet::STORAGE_VERSION);
584 }
585
586 #[test]
587 fn version_range_works() {
588 let range = <(MockMigration<1>, MockMigration<2>)>::VERSION_RANGE;
589 assert_eq!(range, (1, 2));
590 }
591
592 #[test]
593 fn is_upgrade_supported_works() {
594 type Migrations = (MockMigration<9>, MockMigration<10>, MockMigration<11>);
595 assert!(Migrations::is_upgrade_supported(StorageVersion::new(8), StorageVersion::new(11)));
596 assert!(!Migrations::is_upgrade_supported(StorageVersion::new(9), StorageVersion::new(11)));
597 assert!(!Migrations::is_upgrade_supported(StorageVersion::new(8), StorageVersion::new(12)));
598 }
599
600 #[test]
601 fn steps_works() {
602 type Migrations = (MockMigration<2>, MockMigration<3>);
603 let version = StorageVersion::new(2);
604 let mut cursor = Migrations::new(version);
605
606 let mut weight = Weight::from_all(2);
607 let result = Migrations::steps(version, &cursor, &mut weight);
608 cursor = vec![1u8, 0].try_into().unwrap();
609 assert_eq!(result, StepResult::InProgress { cursor: cursor.clone(), steps_done: 1 });
610 assert_eq!(weight, Weight::from_all(1));
611
612 let mut weight = Weight::from_all(2);
613 assert_eq!(
614 Migrations::steps(version, &cursor, &mut weight),
615 StepResult::Completed { steps_done: 1 }
616 );
617 }
618
619 #[test]
620 fn no_migration_in_progress_works() {
621 type TestMigration = Migration<Test>;
622
623 ExtBuilder::default().build().execute_with(|| {
624 assert_eq!(StorageVersion::get::<Pallet<Test>>(), LATEST_MIGRATION_VERSION);
625 assert_eq!(TestMigration::migrate(Weight::MAX).0, MigrateResult::NoMigrationInProgress)
626 });
627 }
628
629 #[test]
630 fn migration_works() {
631 type TestMigration = Migration<Test, false>;
632
633 ExtBuilder::default()
634 .set_storage_version(LATEST_MIGRATION_VERSION - 2)
635 .build()
636 .execute_with(|| {
637 assert_eq!(StorageVersion::get::<Pallet<Test>>(), LATEST_MIGRATION_VERSION - 2);
638 TestMigration::on_runtime_upgrade();
639 for (version, status) in [
640 (LATEST_MIGRATION_VERSION - 1, MigrateResult::InProgress { steps_done: 1 }),
641 (LATEST_MIGRATION_VERSION, MigrateResult::Completed),
642 ] {
643 assert_eq!(TestMigration::migrate(Weight::MAX).0, status);
644 assert_eq!(
645 <Pallet<Test>>::on_chain_storage_version(),
646 StorageVersion::new(version)
647 );
648 }
649
650 assert_eq!(
651 TestMigration::migrate(Weight::MAX).0,
652 MigrateResult::NoMigrationInProgress
653 );
654 assert_eq!(StorageVersion::get::<Pallet<Test>>(), LATEST_MIGRATION_VERSION);
655 });
656 }
657}