1use super::*;
21use alloc::collections::btree_map::BTreeMap;
22use frame_support::{
23 storage_alias,
24 traits::{ConstU32, OnRuntimeUpgrade},
25};
26
27#[cfg(feature = "try-runtime")]
28use frame_support::ensure;
29#[cfg(feature = "try-runtime")]
30use sp_runtime::TryRuntimeError;
31
32const TARGET: &'static str = "runtime::preimage::migration::v1";
34
35mod v0 {
37 use super::*;
38
39 #[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, Debug)]
40 pub enum OldRequestStatus<AccountId, Balance> {
41 Unrequested(Option<(AccountId, Balance)>),
42 Requested(u32),
43 }
44
45 #[storage_alias]
46 pub type PreimageFor<T: Config> = StorageMap<
47 Pallet<T>,
48 Identity,
49 <T as frame_system::Config>::Hash,
50 BoundedVec<u8, ConstU32<MAX_SIZE>>,
51 >;
52
53 #[storage_alias]
54 pub type StatusFor<T: Config> = StorageMap<
55 Pallet<T>,
56 Identity,
57 <T as frame_system::Config>::Hash,
58 OldRequestStatus<<T as frame_system::Config>::AccountId, BalanceOf<T>>,
59 >;
60
61 #[cfg(feature = "try-runtime")]
63 pub fn image_count<T: Config>() -> Option<u32> {
64 let images = v0::PreimageFor::<T>::iter_values().count() as u32;
65 let status = v0::StatusFor::<T>::iter_values().count() as u32;
66
67 if images == status {
68 Some(images)
69 } else {
70 None
71 }
72 }
73}
74
75pub mod v1 {
76 use super::*;
77
78 pub struct Migration<T>(core::marker::PhantomData<T>);
83
84 impl<T: Config> OnRuntimeUpgrade for Migration<T> {
85 #[cfg(feature = "try-runtime")]
86 fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
87 ensure!(StorageVersion::get::<Pallet<T>>() == 0, "can only upgrade from version 0");
88
89 let images = v0::image_count::<T>().expect("v0 storage corrupted");
90 log::info!(target: TARGET, "Migrating {} images", &images);
91 Ok((images as u32).encode())
92 }
93
94 fn on_runtime_upgrade() -> Weight {
95 let mut weight = T::DbWeight::get().reads(1);
96 if StorageVersion::get::<Pallet<T>>() != 0 {
97 log::warn!(
98 target: TARGET,
99 "skipping MovePreimagesIntoBuckets: executed on wrong storage version.\
100 Expected version 0"
101 );
102 return weight;
103 }
104
105 let status = v0::StatusFor::<T>::drain().collect::<Vec<_>>();
106 weight.saturating_accrue(T::DbWeight::get().reads(status.len() as u64));
107
108 let preimages = v0::PreimageFor::<T>::drain().collect::<BTreeMap<_, _>>();
109 weight.saturating_accrue(T::DbWeight::get().reads(preimages.len() as u64));
110
111 for (hash, status) in status.into_iter() {
112 let preimage = if let Some(preimage) = preimages.get(&hash) {
113 preimage
114 } else {
115 log::error!(target: TARGET, "preimage not found for hash {:?}", &hash);
116 continue;
117 };
118 let len = preimage.len() as u32;
119 if len > MAX_SIZE {
120 log::error!(
121 target: TARGET,
122 "preimage too large for hash {:?}, len: {}",
123 &hash,
124 len
125 );
126 continue;
127 }
128
129 let status = match status {
130 v0::OldRequestStatus::Unrequested(deposit) => match deposit {
131 Some(deposit) => OldRequestStatus::Unrequested { deposit, len },
132 None => {
134 OldRequestStatus::Requested { deposit: None, count: 1, len: Some(len) }
135 },
136 },
137 v0::OldRequestStatus::Requested(0) => {
138 log::error!(target: TARGET, "preimage has counter of zero: {:?}", hash);
139 continue;
140 },
141 v0::OldRequestStatus::Requested(count) => {
142 OldRequestStatus::Requested { deposit: None, count, len: Some(len) }
143 },
144 };
145 log::trace!(target: TARGET, "Moving preimage {:?} with len {}", hash, len);
146
147 #[allow(deprecated)]
148 crate::StatusFor::<T>::insert(hash, status);
149 crate::PreimageFor::<T>::insert(&(hash, len), preimage);
150
151 weight.saturating_accrue(T::DbWeight::get().writes(2));
152 }
153 StorageVersion::new(1).put::<Pallet<T>>();
154
155 weight.saturating_add(T::DbWeight::get().writes(1))
156 }
157
158 #[cfg(feature = "try-runtime")]
159 fn post_upgrade(state: Vec<u8>) -> DispatchResult {
160 let old_images: u32 =
161 Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed");
162 let new_images = image_count::<T>().expect("V1 storage corrupted");
163
164 if new_images != old_images {
165 log::error!(
166 target: TARGET,
167 "migrated {} images, expected {}",
168 new_images,
169 old_images
170 );
171 }
172 ensure!(StorageVersion::get::<Pallet<T>>() == 1, "must upgrade");
173 Ok(())
174 }
175 }
176
177 #[cfg(feature = "try-runtime")]
179 pub fn image_count<T: Config>() -> Option<u32> {
180 let images = crate::PreimageFor::<T>::iter_values().count() as u32;
182 #[allow(deprecated)]
183 let status = crate::StatusFor::<T>::iter_values().count() as u32;
184
185 if images == status {
186 Some(images)
187 } else {
188 None
189 }
190 }
191}
192
193#[cfg(test)]
194#[cfg(feature = "try-runtime")]
195mod test {
196 #![allow(deprecated)]
197 use super::*;
198 use crate::mock::{Test as T, *};
199
200 use sp_runtime::bounded_vec;
201
202 #[test]
203 fn migration_works() {
204 new_test_ext().execute_with(|| {
205 assert_eq!(StorageVersion::get::<Pallet<T>>(), 0);
206 let (p, h) = preimage::<T>(128);
210 v0::PreimageFor::<T>::insert(h, p);
211 v0::StatusFor::<T>::insert(h, v0::OldRequestStatus::Unrequested(None));
212 let (p, h) = preimage::<T>(1024);
214 v0::PreimageFor::<T>::insert(h, p);
215 v0::StatusFor::<T>::insert(h, v0::OldRequestStatus::Unrequested(Some((1, 1))));
216 let (p, h) = preimage::<T>(8192);
218 v0::PreimageFor::<T>::insert(h, p);
219 v0::StatusFor::<T>::insert(h, v0::OldRequestStatus::Requested(0));
220 let (p, h) = preimage::<T>(65536);
222 v0::PreimageFor::<T>::insert(h, p);
223 v0::StatusFor::<T>::insert(h, v0::OldRequestStatus::Requested(10));
224
225 assert_eq!(v0::image_count::<T>(), Some(4));
226 assert_eq!(v1::image_count::<T>(), None, "V1 storage should be corrupted");
227
228 let state = v1::Migration::<T>::pre_upgrade().unwrap();
229 let _w = v1::Migration::<T>::on_runtime_upgrade();
230 v1::Migration::<T>::post_upgrade(state).unwrap();
231
232 assert_eq!(v0::image_count::<T>(), Some(3));
234 assert_eq!(v1::image_count::<T>(), Some(3)); assert_eq!(StorageVersion::get::<Pallet<T>>(), 1);
236
237 let (p, h) = preimage::<T>(128);
239 assert_eq!(crate::PreimageFor::<T>::get(&(h, 128)), Some(p));
240 assert_eq!(
241 crate::StatusFor::<T>::get(h),
242 Some(OldRequestStatus::Requested { deposit: None, count: 1, len: Some(128) })
243 );
244 let (p, h) = preimage::<T>(1024);
246 assert_eq!(crate::PreimageFor::<T>::get(&(h, 1024)), Some(p));
247 assert_eq!(
248 crate::StatusFor::<T>::get(h),
249 Some(OldRequestStatus::Unrequested { deposit: (1, 1), len: 1024 })
250 );
251 let (_, h) = preimage::<T>(8192);
253 assert_eq!(crate::PreimageFor::<T>::get(&(h, 8192)), None);
254 assert_eq!(crate::StatusFor::<T>::get(h), None);
255 let (p, h) = preimage::<T>(65536);
257 assert_eq!(crate::PreimageFor::<T>::get(&(h, 65536)), Some(p));
258 assert_eq!(
259 crate::StatusFor::<T>::get(h),
260 Some(OldRequestStatus::Requested { deposit: None, count: 10, len: Some(65536) })
261 );
262 });
263 }
264
265 fn preimage<T: Config>(
267 len: usize,
268 ) -> (BoundedVec<u8, ConstU32<MAX_SIZE>>, <T as frame_system::Config>::Hash) {
269 let p = bounded_vec![1; len];
270 let h = <T as frame_system::Config>::Hashing::hash_of(&p);
271 (p, h)
272 }
273}