1use crate::{
19 defensive,
20 storage::{storage_prefix, transactional::with_transaction_opaque_err},
21 traits::{
22 Defensive, GetStorageVersion, NoStorageVersionSet, PalletInfoAccess, SafeMode,
23 StorageVersion,
24 },
25 weights::{RuntimeDbWeight, Weight, WeightMeter},
26};
27use alloc::vec::Vec;
28use codec::{Decode, Encode, MaxEncodedLen};
29use core::marker::PhantomData;
30use impl_trait_for_tuples::impl_for_tuples;
31use sp_arithmetic::traits::Bounded;
32use sp_core::Get;
33use sp_io::{hashing::twox_128, storage::clear_prefix, KillStorageResult};
34use sp_runtime::traits::Zero;
35
36pub struct VersionedMigration<const FROM: u16, const TO: u16, Inner, Pallet, Weight> {
98 _marker: PhantomData<(Inner, Pallet, Weight)>,
99}
100
101#[derive(Encode, Decode)]
104pub enum VersionedPostUpgradeData {
105 MigrationExecuted(alloc::vec::Vec<u8>),
107 Noop,
109}
110
111impl<
118 const FROM: u16,
119 const TO: u16,
120 Inner: crate::traits::UncheckedOnRuntimeUpgrade,
121 Pallet: GetStorageVersion<InCodeStorageVersion = StorageVersion> + PalletInfoAccess,
122 DbWeight: Get<RuntimeDbWeight>,
123 > crate::traits::OnRuntimeUpgrade for VersionedMigration<FROM, TO, Inner, Pallet, DbWeight>
124{
125 #[cfg(feature = "try-runtime")]
129 fn pre_upgrade() -> Result<alloc::vec::Vec<u8>, sp_runtime::TryRuntimeError> {
130 let on_chain_version = Pallet::on_chain_storage_version();
131 if on_chain_version == FROM {
132 Ok(VersionedPostUpgradeData::MigrationExecuted(Inner::pre_upgrade()?).encode())
133 } else {
134 Ok(VersionedPostUpgradeData::Noop.encode())
135 }
136 }
137
138 fn on_runtime_upgrade() -> Weight {
145 let on_chain_version = Pallet::on_chain_storage_version();
146 if on_chain_version == FROM {
147 log::info!(
148 "๐ Pallet {:?} VersionedMigration migrating storage version from {:?} to {:?}.",
149 Pallet::name(),
150 FROM,
151 TO
152 );
153
154 let weight = Inner::on_runtime_upgrade();
156
157 StorageVersion::new(TO).put::<Pallet>();
159
160 weight.saturating_add(DbWeight::get().reads_writes(1, 1))
161 } else {
162 log::warn!(
163 "๐ Pallet {:?} VersionedMigration migration {}->{} can be removed; on-chain is already at {:?}.",
164 Pallet::name(),
165 FROM,
166 TO,
167 on_chain_version
168 );
169 DbWeight::get().reads(1)
170 }
171 }
172
173 #[cfg(feature = "try-runtime")]
178 fn post_upgrade(
179 versioned_post_upgrade_data_bytes: alloc::vec::Vec<u8>,
180 ) -> Result<(), sp_runtime::TryRuntimeError> {
181 use codec::DecodeAll;
182 match <VersionedPostUpgradeData>::decode_all(&mut &versioned_post_upgrade_data_bytes[..])
183 .map_err(|_| "VersionedMigration post_upgrade failed to decode PreUpgradeData")?
184 {
185 VersionedPostUpgradeData::MigrationExecuted(inner_bytes) =>
186 Inner::post_upgrade(inner_bytes),
187 VersionedPostUpgradeData::Noop => Ok(()),
188 }
189 }
190}
191
192pub trait StoreInCodeStorageVersion<T: GetStorageVersion + PalletInfoAccess> {
194 fn store_in_code_storage_version();
196}
197
198impl<T: GetStorageVersion<InCodeStorageVersion = StorageVersion> + PalletInfoAccess>
199 StoreInCodeStorageVersion<T> for StorageVersion
200{
201 fn store_in_code_storage_version() {
202 let version = <T as GetStorageVersion>::in_code_storage_version();
203 version.put::<T>();
204 }
205}
206
207impl<T: GetStorageVersion<InCodeStorageVersion = NoStorageVersionSet> + PalletInfoAccess>
208 StoreInCodeStorageVersion<T> for NoStorageVersionSet
209{
210 fn store_in_code_storage_version() {
211 StorageVersion::default().put::<T>();
212 }
213}
214
215pub trait PalletVersionToStorageVersionHelper {
217 fn migrate(db_weight: &RuntimeDbWeight) -> Weight;
218}
219
220impl<T: GetStorageVersion + PalletInfoAccess> PalletVersionToStorageVersionHelper for T
221where
222 T::InCodeStorageVersion: StoreInCodeStorageVersion<T>,
223{
224 fn migrate(db_weight: &RuntimeDbWeight) -> Weight {
225 const PALLET_VERSION_STORAGE_KEY_POSTFIX: &[u8] = b":__PALLET_VERSION__:";
226
227 fn pallet_version_key(name: &str) -> [u8; 32] {
228 crate::storage::storage_prefix(name.as_bytes(), PALLET_VERSION_STORAGE_KEY_POSTFIX)
229 }
230
231 sp_io::storage::clear(&pallet_version_key(<T as PalletInfoAccess>::name()));
232
233 <T::InCodeStorageVersion as StoreInCodeStorageVersion<T>>::store_in_code_storage_version();
234
235 db_weight.writes(2)
236 }
237}
238
239#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))]
240#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))]
241#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))]
242impl PalletVersionToStorageVersionHelper for T {
243 fn migrate(db_weight: &RuntimeDbWeight) -> Weight {
244 let mut weight = Weight::zero();
245
246 for_tuples!( #( weight = weight.saturating_add(T::migrate(db_weight)); )* );
247
248 weight
249 }
250}
251
252pub fn migrate_from_pallet_version_to_storage_version<
256 Pallets: PalletVersionToStorageVersionHelper,
257>(
258 db_weight: &RuntimeDbWeight,
259) -> Weight {
260 Pallets::migrate(db_weight)
261}
262
263pub struct RemovePallet<P: Get<&'static str>, DbWeight: Get<RuntimeDbWeight>>(
319 PhantomData<(P, DbWeight)>,
320);
321impl<P: Get<&'static str>, DbWeight: Get<RuntimeDbWeight>> frame_support::traits::OnRuntimeUpgrade
322 for RemovePallet<P, DbWeight>
323{
324 fn on_runtime_upgrade() -> frame_support::weights::Weight {
325 let hashed_prefix = twox_128(P::get().as_bytes());
326 let keys_removed = match clear_prefix(&hashed_prefix, None) {
327 KillStorageResult::AllRemoved(value) => value,
328 KillStorageResult::SomeRemaining(value) => {
329 log::error!(
330 "`clear_prefix` failed to remove all keys for {}. THIS SHOULD NEVER HAPPEN! ๐จ",
331 P::get()
332 );
333 value
334 },
335 } as u64;
336
337 log::info!("Removed {} {} keys ๐งน", keys_removed, P::get());
338
339 DbWeight::get().reads_writes(keys_removed + 1, keys_removed)
340 }
341
342 #[cfg(feature = "try-runtime")]
343 fn pre_upgrade() -> Result<alloc::vec::Vec<u8>, sp_runtime::TryRuntimeError> {
344 use crate::storage::unhashed::contains_prefixed_key;
345
346 let hashed_prefix = twox_128(P::get().as_bytes());
347 match contains_prefixed_key(&hashed_prefix) {
348 true => log::info!("Found {} keys pre-removal ๐", P::get()),
349 false => log::warn!(
350 "Migration RemovePallet<{}> can be removed (no keys found pre-removal).",
351 P::get()
352 ),
353 };
354 Ok(alloc::vec::Vec::new())
355 }
356
357 #[cfg(feature = "try-runtime")]
358 fn post_upgrade(_state: alloc::vec::Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
359 use crate::storage::unhashed::contains_prefixed_key;
360
361 let hashed_prefix = twox_128(P::get().as_bytes());
362 match contains_prefixed_key(&hashed_prefix) {
363 true => {
364 log::error!("{} has keys remaining post-removal โ", P::get());
365 return Err("Keys remaining post-removal, this should never happen ๐จ".into())
366 },
367 false => log::info!("No {} keys found post-removal ๐", P::get()),
368 };
369 Ok(())
370 }
371}
372
373pub struct RemoveStorage<P: Get<&'static str>, S: Get<&'static str>, DbWeight: Get<RuntimeDbWeight>>(
430 PhantomData<(P, S, DbWeight)>,
431);
432impl<P: Get<&'static str>, S: Get<&'static str>, DbWeight: Get<RuntimeDbWeight>>
433 frame_support::traits::OnRuntimeUpgrade for RemoveStorage<P, S, DbWeight>
434{
435 fn on_runtime_upgrade() -> frame_support::weights::Weight {
436 let hashed_prefix = storage_prefix(P::get().as_bytes(), S::get().as_bytes());
437 let keys_removed = match clear_prefix(&hashed_prefix, None) {
438 KillStorageResult::AllRemoved(value) => value,
439 KillStorageResult::SomeRemaining(value) => {
440 log::error!(
441 "`clear_prefix` failed to remove all keys for storage `{}` from pallet `{}`. THIS SHOULD NEVER HAPPEN! ๐จ",
442 S::get(), P::get()
443 );
444 value
445 },
446 } as u64;
447
448 log::info!("Removed `{}` `{}` `{}` keys ๐งน", keys_removed, P::get(), S::get());
449
450 DbWeight::get().reads_writes(keys_removed + 1, keys_removed)
451 }
452
453 #[cfg(feature = "try-runtime")]
454 fn pre_upgrade() -> Result<alloc::vec::Vec<u8>, sp_runtime::TryRuntimeError> {
455 use crate::storage::unhashed::contains_prefixed_key;
456
457 let hashed_prefix = storage_prefix(P::get().as_bytes(), S::get().as_bytes());
458 match contains_prefixed_key(&hashed_prefix) {
459 true => log::info!("Found `{}` `{}` keys pre-removal ๐", P::get(), S::get()),
460 false => log::warn!(
461 "Migration RemoveStorage<{}, {}> can be removed (no keys found pre-removal).",
462 P::get(),
463 S::get()
464 ),
465 };
466 Ok(Default::default())
467 }
468
469 #[cfg(feature = "try-runtime")]
470 fn post_upgrade(_state: alloc::vec::Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
471 use crate::storage::unhashed::contains_prefixed_key;
472
473 let hashed_prefix = storage_prefix(P::get().as_bytes(), S::get().as_bytes());
474 match contains_prefixed_key(&hashed_prefix) {
475 true => {
476 log::error!("`{}` `{}` has keys remaining post-removal โ", P::get(), S::get());
477 return Err("Keys remaining post-removal, this should never happen ๐จ".into())
478 },
479 false => log::info!("No `{}` `{}` keys found post-removal ๐", P::get(), S::get()),
480 };
481 Ok(())
482 }
483}
484
485pub trait SteppedMigration {
487 type Cursor: codec::FullCodec + codec::MaxEncodedLen;
489
490 type Identifier: codec::FullCodec + codec::MaxEncodedLen;
492
493 fn id() -> Self::Identifier;
497
498 fn max_steps() -> Option<u32> {
504 None
505 }
506
507 fn step(
514 cursor: Option<Self::Cursor>,
515 meter: &mut WeightMeter,
516 ) -> Result<Option<Self::Cursor>, SteppedMigrationError>;
517
518 fn transactional_step(
520 mut cursor: Option<Self::Cursor>,
521 meter: &mut WeightMeter,
522 ) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
523 with_transaction_opaque_err(move || match Self::step(cursor, meter) {
524 Ok(new_cursor) => {
525 cursor = new_cursor;
526 sp_runtime::TransactionOutcome::Commit(Ok(cursor))
527 },
528 Err(err) => sp_runtime::TransactionOutcome::Rollback(Err(err)),
529 })
530 .map_err(|()| SteppedMigrationError::Failed)?
531 }
532
533 #[cfg(feature = "try-runtime")]
538 fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
539 Ok(Vec::new())
540 }
541
542 #[cfg(feature = "try-runtime")]
548 fn post_upgrade(_state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
549 Ok(())
550 }
551}
552
553#[derive(Debug, Encode, Decode, MaxEncodedLen, scale_info::TypeInfo)]
555pub enum SteppedMigrationError {
556 InsufficientWeight {
562 required: Weight,
564 },
565 InvalidCursor,
572 Failed,
574}
575
576#[derive(MaxEncodedLen, Encode, Decode)]
580pub struct MigrationId<const N: usize> {
581 pub pallet_id: [u8; N],
582 pub version_from: u8,
583 pub version_to: u8,
584}
585
586#[impl_trait_for_tuples::impl_for_tuples(8)]
588pub trait MigrationStatusHandler {
589 fn started() {}
591
592 fn completed() {}
594}
595
596pub trait FailedMigrationHandler {
600 fn failed(migration: Option<u32>) -> FailedMigrationHandling;
606}
607
608pub struct FreezeChainOnFailedMigration;
612
613impl FailedMigrationHandler for FreezeChainOnFailedMigration {
614 fn failed(_migration: Option<u32>) -> FailedMigrationHandling {
615 FailedMigrationHandling::KeepStuck
616 }
617}
618
619pub struct EnterSafeModeOnFailedMigration<SM, Else: FailedMigrationHandler>(
624 PhantomData<(SM, Else)>,
625);
626
627impl<Else: FailedMigrationHandler, SM: SafeMode> FailedMigrationHandler
628 for EnterSafeModeOnFailedMigration<SM, Else>
629where
630 <SM as SafeMode>::BlockNumber: Bounded,
631{
632 fn failed(migration: Option<u32>) -> FailedMigrationHandling {
633 let entered = if SM::is_entered() {
634 SM::extend(Bounded::max_value())
635 } else {
636 SM::enter(Bounded::max_value())
637 };
638
639 if entered.is_err() {
641 Else::failed(migration)
642 } else {
643 FailedMigrationHandling::KeepStuck
644 }
645 }
646}
647
648#[derive(Debug, Clone, Copy, PartialEq, Eq)]
652pub enum FailedMigrationHandling {
653 ForceUnstuck,
658 KeepStuck,
660 Ignore,
665}
666
667pub trait MultiStepMigrator {
669 fn ongoing() -> bool;
671
672 fn step() -> Weight;
676}
677
678impl MultiStepMigrator for () {
679 fn ongoing() -> bool {
680 false
681 }
682
683 fn step() -> Weight {
684 Weight::zero()
685 }
686}
687
688pub trait SteppedMigrations {
690 fn len() -> u32;
692
693 fn nth_id(n: u32) -> Option<Vec<u8>>;
698
699 fn nth_max_steps(n: u32) -> Option<Option<u32>>;
703
704 fn nth_step(
708 n: u32,
709 cursor: Option<Vec<u8>>,
710 meter: &mut WeightMeter,
711 ) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>>;
712
713 fn nth_transactional_step(
717 n: u32,
718 cursor: Option<Vec<u8>>,
719 meter: &mut WeightMeter,
720 ) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>>;
721
722 #[cfg(feature = "try-runtime")]
726 fn nth_pre_upgrade(n: u32) -> Option<Result<Vec<u8>, sp_runtime::TryRuntimeError>>;
727
728 #[cfg(feature = "try-runtime")]
732 fn nth_post_upgrade(n: u32, _state: Vec<u8>)
733 -> Option<Result<(), sp_runtime::TryRuntimeError>>;
734
735 fn cursor_max_encoded_len() -> usize;
737
738 fn identifier_max_encoded_len() -> usize;
740
741 #[cfg(feature = "std")]
746 fn integrity_test() -> Result<(), &'static str> {
747 use crate::ensure;
748 let l = Self::len();
749
750 for n in 0..l {
751 ensure!(Self::nth_id(n).is_some(), "id is None");
752 ensure!(Self::nth_max_steps(n).is_some(), "steps is None");
753
754 ensure!(
756 Self::nth_step(n, Some(vec![]), &mut WeightMeter::new()).is_some(),
757 "steps is None"
758 );
759 ensure!(
760 Self::nth_transactional_step(n, Some(vec![]), &mut WeightMeter::new()).is_some(),
761 "steps is None"
762 );
763 }
764
765 Ok(())
766 }
767}
768
769impl SteppedMigrations for () {
770 fn len() -> u32 {
771 0
772 }
773
774 fn nth_id(_n: u32) -> Option<Vec<u8>> {
775 None
776 }
777
778 fn nth_max_steps(_n: u32) -> Option<Option<u32>> {
779 None
780 }
781
782 fn nth_step(
783 _n: u32,
784 _cursor: Option<Vec<u8>>,
785 _meter: &mut WeightMeter,
786 ) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>> {
787 None
788 }
789
790 fn nth_transactional_step(
791 _n: u32,
792 _cursor: Option<Vec<u8>>,
793 _meter: &mut WeightMeter,
794 ) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>> {
795 None
796 }
797
798 #[cfg(feature = "try-runtime")]
799 fn nth_pre_upgrade(_n: u32) -> Option<Result<Vec<u8>, sp_runtime::TryRuntimeError>> {
800 Some(Ok(Vec::new()))
801 }
802
803 #[cfg(feature = "try-runtime")]
804 fn nth_post_upgrade(
805 _n: u32,
806 _state: Vec<u8>,
807 ) -> Option<Result<(), sp_runtime::TryRuntimeError>> {
808 Some(Ok(()))
809 }
810
811 fn cursor_max_encoded_len() -> usize {
812 0
813 }
814
815 fn identifier_max_encoded_len() -> usize {
816 0
817 }
818}
819
820impl<T: SteppedMigration> SteppedMigrations for T {
822 fn len() -> u32 {
823 1
824 }
825
826 fn nth_id(n: u32) -> Option<Vec<u8>> {
827 n.is_zero()
828 .then(|| T::id().encode())
829 .defensive_proof("nth_id should only be called with n==0")
830 }
831
832 fn nth_max_steps(n: u32) -> Option<Option<u32>> {
833 n.is_zero()
835 .then(|| T::max_steps())
836 .defensive_proof("nth_max_steps should only be called with n==0")
837 }
838
839 fn nth_step(
840 n: u32,
841 cursor: Option<Vec<u8>>,
842 meter: &mut WeightMeter,
843 ) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>> {
844 if !n.is_zero() {
845 defensive!("nth_step should only be called with n==0");
846 return None
847 }
848
849 let cursor = match cursor {
850 Some(cursor) => match T::Cursor::decode(&mut &cursor[..]) {
851 Ok(cursor) => Some(cursor),
852 Err(_) => return Some(Err(SteppedMigrationError::InvalidCursor)),
853 },
854 None => None,
855 };
856
857 Some(T::step(cursor, meter).map(|cursor| cursor.map(|cursor| cursor.encode())))
858 }
859
860 fn nth_transactional_step(
861 n: u32,
862 cursor: Option<Vec<u8>>,
863 meter: &mut WeightMeter,
864 ) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>> {
865 if n != 0 {
866 defensive!("nth_transactional_step should only be called with n==0");
867 return None
868 }
869
870 let cursor = match cursor {
871 Some(cursor) => match T::Cursor::decode(&mut &cursor[..]) {
872 Ok(cursor) => Some(cursor),
873 Err(_) => return Some(Err(SteppedMigrationError::InvalidCursor)),
874 },
875 None => None,
876 };
877
878 Some(
879 T::transactional_step(cursor, meter).map(|cursor| cursor.map(|cursor| cursor.encode())),
880 )
881 }
882
883 #[cfg(feature = "try-runtime")]
884 fn nth_pre_upgrade(n: u32) -> Option<Result<Vec<u8>, sp_runtime::TryRuntimeError>> {
885 if n != 0 {
886 defensive!("nth_pre_upgrade should only be called with n==0");
887 }
888
889 Some(T::pre_upgrade())
890 }
891
892 #[cfg(feature = "try-runtime")]
893 fn nth_post_upgrade(n: u32, state: Vec<u8>) -> Option<Result<(), sp_runtime::TryRuntimeError>> {
894 if n != 0 {
895 defensive!("nth_post_upgrade should only be called with n==0");
896 }
897 Some(T::post_upgrade(state))
898 }
899
900 fn cursor_max_encoded_len() -> usize {
901 T::Cursor::max_encoded_len()
902 }
903
904 fn identifier_max_encoded_len() -> usize {
905 T::Identifier::max_encoded_len()
906 }
907}
908
909#[impl_trait_for_tuples::impl_for_tuples(1, 30)]
910impl SteppedMigrations for Tuple {
911 fn len() -> u32 {
912 for_tuples!( #( Tuple::len() )+* )
913 }
914
915 fn nth_id(n: u32) -> Option<Vec<u8>> {
916 let mut i = 0;
917
918 for_tuples!( #(
919 if (i + Tuple::len()) > n {
920 return Tuple::nth_id(n - i)
921 }
922
923 i += Tuple::len();
924 )* );
925
926 None
927 }
928
929 fn nth_step(
930 n: u32,
931 cursor: Option<Vec<u8>>,
932 meter: &mut WeightMeter,
933 ) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>> {
934 let mut i = 0;
935
936 for_tuples!( #(
937 if (i + Tuple::len()) > n {
938 return Tuple::nth_step(n - i, cursor, meter)
939 }
940
941 i += Tuple::len();
942 )* );
943
944 None
945 }
946
947 fn nth_transactional_step(
948 n: u32,
949 cursor: Option<Vec<u8>>,
950 meter: &mut WeightMeter,
951 ) -> Option<Result<Option<Vec<u8>>, SteppedMigrationError>> {
952 let mut i = 0;
953
954 for_tuples! ( #(
955 if (i + Tuple::len()) > n {
956 return Tuple::nth_transactional_step(n - i, cursor, meter)
957 }
958
959 i += Tuple::len();
960 )* );
961
962 None
963 }
964
965 #[cfg(feature = "try-runtime")]
966 fn nth_pre_upgrade(n: u32) -> Option<Result<Vec<u8>, sp_runtime::TryRuntimeError>> {
967 let mut i = 0;
968
969 for_tuples! ( #(
970 if (i + Tuple::len()) > n {
971 return Tuple::nth_pre_upgrade(n - i)
972 }
973
974 i += Tuple::len();
975 )* );
976
977 None
978 }
979
980 #[cfg(feature = "try-runtime")]
981 fn nth_post_upgrade(n: u32, state: Vec<u8>) -> Option<Result<(), sp_runtime::TryRuntimeError>> {
982 let mut i = 0;
983
984 for_tuples! ( #(
985 if (i + Tuple::len()) > n {
986 return Tuple::nth_post_upgrade(n - i, state)
987 }
988
989 i += Tuple::len();
990 )* );
991
992 None
993 }
994
995 fn nth_max_steps(n: u32) -> Option<Option<u32>> {
996 let mut i = 0;
997
998 for_tuples!( #(
999 if (i + Tuple::len()) > n {
1000 return Tuple::nth_max_steps(n - i)
1001 }
1002
1003 i += Tuple::len();
1004 )* );
1005
1006 None
1007 }
1008
1009 fn cursor_max_encoded_len() -> usize {
1010 let mut max_len = 0;
1011
1012 for_tuples!( #(
1013 max_len = max_len.max(Tuple::cursor_max_encoded_len());
1014 )* );
1015
1016 max_len
1017 }
1018
1019 fn identifier_max_encoded_len() -> usize {
1020 let mut max_len = 0;
1021
1022 for_tuples!( #(
1023 max_len = max_len.max(Tuple::identifier_max_encoded_len());
1024 )* );
1025
1026 max_len
1027 }
1028}
1029
1030#[cfg(test)]
1031mod tests {
1032 use super::*;
1033 use crate::{assert_ok, storage::unhashed};
1034
1035 #[derive(Decode, Encode, MaxEncodedLen, Eq, PartialEq)]
1036 pub enum Either<L, R> {
1037 Left(L),
1038 Right(R),
1039 }
1040
1041 pub struct M0;
1042 impl SteppedMigration for M0 {
1043 type Cursor = ();
1044 type Identifier = u8;
1045
1046 fn id() -> Self::Identifier {
1047 0
1048 }
1049
1050 fn step(
1051 _cursor: Option<Self::Cursor>,
1052 _meter: &mut WeightMeter,
1053 ) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
1054 log::info!("M0");
1055 unhashed::put(&[0], &());
1056 Ok(None)
1057 }
1058 }
1059
1060 pub struct M1;
1061 impl SteppedMigration for M1 {
1062 type Cursor = ();
1063 type Identifier = u8;
1064
1065 fn id() -> Self::Identifier {
1066 1
1067 }
1068
1069 fn step(
1070 _cursor: Option<Self::Cursor>,
1071 _meter: &mut WeightMeter,
1072 ) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
1073 log::info!("M1");
1074 unhashed::put(&[1], &());
1075 Ok(None)
1076 }
1077
1078 fn max_steps() -> Option<u32> {
1079 Some(1)
1080 }
1081 }
1082
1083 pub struct M2;
1084 impl SteppedMigration for M2 {
1085 type Cursor = ();
1086 type Identifier = u8;
1087
1088 fn id() -> Self::Identifier {
1089 2
1090 }
1091
1092 fn step(
1093 _cursor: Option<Self::Cursor>,
1094 _meter: &mut WeightMeter,
1095 ) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
1096 log::info!("M2");
1097 unhashed::put(&[2], &());
1098 Ok(None)
1099 }
1100
1101 fn max_steps() -> Option<u32> {
1102 Some(2)
1103 }
1104 }
1105
1106 pub struct F0;
1107 impl SteppedMigration for F0 {
1108 type Cursor = ();
1109 type Identifier = u8;
1110
1111 fn id() -> Self::Identifier {
1112 3
1113 }
1114
1115 fn step(
1116 _cursor: Option<Self::Cursor>,
1117 _meter: &mut WeightMeter,
1118 ) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
1119 log::info!("F0");
1120 unhashed::put(&[3], &());
1121 Err(SteppedMigrationError::Failed)
1122 }
1123 }
1124
1125 type Triple = (M0, (M1, M2));
1127 type Hextuple = (Triple, Triple);
1129
1130 #[test]
1131 fn singular_migrations_work() {
1132 assert_eq!(M0::max_steps(), None);
1133 assert_eq!(M1::max_steps(), Some(1));
1134 assert_eq!(M2::max_steps(), Some(2));
1135
1136 assert_eq!(<(M0, M1)>::nth_max_steps(0), Some(None));
1137 assert_eq!(<(M0, M1)>::nth_max_steps(1), Some(Some(1)));
1138 assert_eq!(<(M0, M1, M2)>::nth_max_steps(2), Some(Some(2)));
1139
1140 assert_eq!(<(M0, M1)>::nth_max_steps(2), None);
1141 }
1142
1143 #[test]
1144 fn tuple_migrations_work() {
1145 assert_eq!(<() as SteppedMigrations>::len(), 0);
1146 assert_eq!(<((), ((), ())) as SteppedMigrations>::len(), 0);
1147 assert_eq!(<Triple as SteppedMigrations>::len(), 3);
1148 assert_eq!(<Hextuple as SteppedMigrations>::len(), 6);
1149
1150 assert_eq!(<Triple as SteppedMigrations>::nth_id(0), Some(0u8.encode()));
1153 assert_eq!(<Triple as SteppedMigrations>::nth_id(1), Some(1u8.encode()));
1154 assert_eq!(<Triple as SteppedMigrations>::nth_id(2), Some(2u8.encode()));
1155
1156 sp_io::TestExternalities::default().execute_with(|| {
1157 for n in 0..3 {
1158 <Triple as SteppedMigrations>::nth_step(
1159 n,
1160 Default::default(),
1161 &mut WeightMeter::new(),
1162 );
1163 }
1164 });
1165 }
1166
1167 #[test]
1168 fn integrity_test_works() {
1169 sp_io::TestExternalities::default().execute_with(|| {
1170 assert_ok!(<() as SteppedMigrations>::integrity_test());
1171 assert_ok!(<M0 as SteppedMigrations>::integrity_test());
1172 assert_ok!(<M1 as SteppedMigrations>::integrity_test());
1173 assert_ok!(<M2 as SteppedMigrations>::integrity_test());
1174 assert_ok!(<Triple as SteppedMigrations>::integrity_test());
1175 assert_ok!(<Hextuple as SteppedMigrations>::integrity_test());
1176 });
1177 }
1178
1179 #[test]
1180 fn transactional_rollback_works() {
1181 sp_io::TestExternalities::default().execute_with(|| {
1182 assert_ok!(<(M0, F0) as SteppedMigrations>::nth_transactional_step(
1183 0,
1184 Default::default(),
1185 &mut WeightMeter::new()
1186 )
1187 .unwrap());
1188 assert!(unhashed::exists(&[0]));
1189
1190 let _g = crate::StorageNoopGuard::new();
1191 assert!(<(M0, F0) as SteppedMigrations>::nth_transactional_step(
1192 1,
1193 Default::default(),
1194 &mut WeightMeter::new()
1195 )
1196 .unwrap()
1197 .is_err());
1198 assert!(<(F0, M1) as SteppedMigrations>::nth_transactional_step(
1199 0,
1200 Default::default(),
1201 &mut WeightMeter::new()
1202 )
1203 .unwrap()
1204 .is_err());
1205 });
1206 }
1207}