Skip to main content

topsoil_core/storage/
migration.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! Some utilities for helping access storage with arbitrary key types.
8
9use crate::{
10	hash::ReversibleStorageHasher,
11	storage::{storage_prefix, unhashed},
12	StorageHasher, Twox128,
13};
14use alloc::{vec, vec::Vec};
15use codec::{Decode, Encode};
16
17use super::PrefixIterator;
18
19/// Utility to iterate through raw items in storage.
20pub struct StorageIterator<T> {
21	prefix: Vec<u8>,
22	previous_key: Vec<u8>,
23	drain: bool,
24	_phantom: ::core::marker::PhantomData<T>,
25}
26
27impl<T> StorageIterator<T> {
28	/// Construct iterator to iterate over map items in `module` for the map called `item`.
29	#[deprecated(note = "Will be removed after July 2023; Please use the storage_iter or \
30		storage_iter_with_suffix functions instead")]
31	pub fn new(module: &[u8], item: &[u8]) -> Self {
32		#[allow(deprecated)]
33		Self::with_suffix(module, item, &[][..])
34	}
35
36	/// Construct iterator to iterate over map items in `module` for the map called `item`.
37	#[deprecated(note = "Will be removed after July 2023; Please use the storage_iter or \
38		storage_iter_with_suffix functions instead")]
39	pub fn with_suffix(module: &[u8], item: &[u8], suffix: &[u8]) -> Self {
40		let mut prefix = Vec::new();
41		let storage_prefix = storage_prefix(module, item);
42		prefix.extend_from_slice(&storage_prefix);
43		prefix.extend_from_slice(suffix);
44		let previous_key = prefix.clone();
45		Self { prefix, previous_key, drain: false, _phantom: Default::default() }
46	}
47
48	/// Mutate this iterator into a draining iterator; items iterated are removed from storage.
49	pub fn drain(mut self) -> Self {
50		self.drain = true;
51		self
52	}
53}
54
55impl<T: Decode + Sized> Iterator for StorageIterator<T> {
56	type Item = (Vec<u8>, T);
57
58	fn next(&mut self) -> Option<(Vec<u8>, T)> {
59		loop {
60			let maybe_next = subsoil::io::storage::next_key(&self.previous_key)
61				.filter(|n| n.starts_with(&self.prefix));
62			break match maybe_next {
63				Some(next) => {
64					self.previous_key = next.clone();
65					let maybe_value = topsoil_core::storage::unhashed::get::<T>(&next);
66					match maybe_value {
67						Some(value) => {
68							if self.drain {
69								topsoil_core::storage::unhashed::kill(&next);
70							}
71							Some((self.previous_key[self.prefix.len()..].to_vec(), value))
72						},
73						None => continue,
74					}
75				},
76				None => None,
77			};
78		}
79	}
80}
81
82/// Utility to iterate through raw items in storage.
83pub struct StorageKeyIterator<K, T, H: ReversibleStorageHasher> {
84	prefix: Vec<u8>,
85	previous_key: Vec<u8>,
86	drain: bool,
87	_phantom: ::core::marker::PhantomData<(K, T, H)>,
88}
89
90impl<K, T, H: ReversibleStorageHasher> StorageKeyIterator<K, T, H> {
91	/// Construct iterator to iterate over map items in `module` for the map called `item`.
92	#[deprecated(note = "Will be removed after July 2023; Please use the storage_key_iter or \
93		storage_key_iter_with_suffix functions instead")]
94	pub fn new(module: &[u8], item: &[u8]) -> Self {
95		#[allow(deprecated)]
96		Self::with_suffix(module, item, &[][..])
97	}
98
99	/// Construct iterator to iterate over map items in `module` for the map called `item`.
100	#[deprecated(note = "Will be removed after July 2023; Please use the storage_key_iter or \
101		storage_key_iter_with_suffix functions instead")]
102	pub fn with_suffix(module: &[u8], item: &[u8], suffix: &[u8]) -> Self {
103		let mut prefix = Vec::new();
104		let storage_prefix = storage_prefix(module, item);
105		prefix.extend_from_slice(&storage_prefix);
106		prefix.extend_from_slice(suffix);
107		let previous_key = prefix.clone();
108		Self { prefix, previous_key, drain: false, _phantom: Default::default() }
109	}
110
111	/// Mutate this iterator into a draining iterator; items iterated are removed from storage.
112	pub fn drain(mut self) -> Self {
113		self.drain = true;
114		self
115	}
116}
117
118impl<K: Decode + Sized, T: Decode + Sized, H: ReversibleStorageHasher> Iterator
119	for StorageKeyIterator<K, T, H>
120{
121	type Item = (K, T);
122
123	fn next(&mut self) -> Option<(K, T)> {
124		loop {
125			let maybe_next = subsoil::io::storage::next_key(&self.previous_key)
126				.filter(|n| n.starts_with(&self.prefix));
127			break match maybe_next {
128				Some(next) => {
129					self.previous_key = next.clone();
130					let mut key_material = H::reverse(&next[self.prefix.len()..]);
131					match K::decode(&mut key_material) {
132						Ok(key) => {
133							let maybe_value = topsoil_core::storage::unhashed::get::<T>(&next);
134							match maybe_value {
135								Some(value) => {
136									if self.drain {
137										topsoil_core::storage::unhashed::kill(&next);
138									}
139									Some((key, value))
140								},
141								None => continue,
142							}
143						},
144						Err(_) => continue,
145					}
146				},
147				None => None,
148			};
149		}
150	}
151}
152
153/// Construct iterator to iterate over map items in `module` for the map called `item`.
154pub fn storage_iter<T: Decode + Sized>(module: &[u8], item: &[u8]) -> PrefixIterator<(Vec<u8>, T)> {
155	storage_iter_with_suffix(module, item, &[][..])
156}
157
158/// Construct iterator to iterate over map items in `module` for the map called `item`.
159pub fn storage_iter_with_suffix<T: Decode + Sized>(
160	module: &[u8],
161	item: &[u8],
162	suffix: &[u8],
163) -> PrefixIterator<(Vec<u8>, T)> {
164	let mut prefix = Vec::new();
165	let storage_prefix = storage_prefix(module, item);
166	prefix.extend_from_slice(&storage_prefix);
167	prefix.extend_from_slice(suffix);
168	let previous_key = prefix.clone();
169	let closure = |raw_key_without_prefix: &[u8], mut raw_value: &[u8]| {
170		let value = T::decode(&mut raw_value)?;
171		Ok((raw_key_without_prefix.to_vec(), value))
172	};
173
174	PrefixIterator { prefix, previous_key, drain: false, closure, phantom: Default::default() }
175}
176
177/// Construct iterator to iterate over map items in `module` for the map called `item`.
178pub fn storage_key_iter<K: Decode + Sized, T: Decode + Sized, H: ReversibleStorageHasher>(
179	module: &[u8],
180	item: &[u8],
181) -> PrefixIterator<(K, T)> {
182	storage_key_iter_with_suffix::<K, T, H>(module, item, &[][..])
183}
184
185/// Construct iterator to iterate over map items in `module` for the map called `item`.
186pub fn storage_key_iter_with_suffix<
187	K: Decode + Sized,
188	T: Decode + Sized,
189	H: ReversibleStorageHasher,
190>(
191	module: &[u8],
192	item: &[u8],
193	suffix: &[u8],
194) -> PrefixIterator<(K, T)> {
195	let mut prefix = Vec::new();
196	let storage_prefix = storage_prefix(module, item);
197
198	prefix.extend_from_slice(&storage_prefix);
199	prefix.extend_from_slice(suffix);
200	let previous_key = prefix.clone();
201	let closure = |raw_key_without_prefix: &[u8], mut raw_value: &[u8]| {
202		let mut key_material = H::reverse(raw_key_without_prefix);
203		let key = K::decode(&mut key_material)?;
204		let value = T::decode(&mut raw_value)?;
205		Ok((key, value))
206	};
207	PrefixIterator { prefix, previous_key, drain: false, closure, phantom: Default::default() }
208}
209
210/// Get a particular value in storage by the `module`, the map's `item` name and the key `hash`.
211pub fn have_storage_value(module: &[u8], item: &[u8], hash: &[u8]) -> bool {
212	get_storage_value::<()>(module, item, hash).is_some()
213}
214
215/// Get a particular value in storage by the `module`, the map's `item` name and the key `hash`.
216pub fn get_storage_value<T: Decode + Sized>(module: &[u8], item: &[u8], hash: &[u8]) -> Option<T> {
217	let mut key = vec![0u8; 32 + hash.len()];
218	let storage_prefix = storage_prefix(module, item);
219	key[0..32].copy_from_slice(&storage_prefix);
220	key[32..].copy_from_slice(hash);
221	topsoil_core::storage::unhashed::get::<T>(&key)
222}
223
224/// Take a particular value in storage by the `module`, the map's `item` name and the key `hash`.
225pub fn take_storage_value<T: Decode + Sized>(module: &[u8], item: &[u8], hash: &[u8]) -> Option<T> {
226	let mut key = vec![0u8; 32 + hash.len()];
227	let storage_prefix = storage_prefix(module, item);
228	key[0..32].copy_from_slice(&storage_prefix);
229	key[32..].copy_from_slice(hash);
230	topsoil_core::storage::unhashed::take::<T>(&key)
231}
232
233/// Put a particular value into storage by the `module`, the map's `item` name and the key `hash`.
234pub fn put_storage_value<T: Encode>(module: &[u8], item: &[u8], hash: &[u8], value: T) {
235	let mut key = vec![0u8; 32 + hash.len()];
236	let storage_prefix = storage_prefix(module, item);
237	key[0..32].copy_from_slice(&storage_prefix);
238	key[32..].copy_from_slice(hash);
239	topsoil_core::storage::unhashed::put(&key, &value);
240}
241
242/// Remove all items under a storage prefix by the `module`, the map's `item` name and the key
243/// `hash`.
244#[deprecated = "Use `clear_storage_prefix` instead"]
245pub fn remove_storage_prefix(module: &[u8], item: &[u8], hash: &[u8]) {
246	let mut key = vec![0u8; 32 + hash.len()];
247	let storage_prefix = storage_prefix(module, item);
248	key[0..32].copy_from_slice(&storage_prefix);
249	key[32..].copy_from_slice(hash);
250	let _ = topsoil_core::storage::unhashed::clear_prefix(&key, None, None);
251}
252
253/// Attempt to remove all values under a storage prefix by the `module`, the map's `item` name and
254/// the key `hash`.
255///
256/// All values in the client overlay will be deleted, if `maybe_limit` is `Some` then up to
257/// that number of values are deleted from the client backend by seeking and reading that number of
258/// storage values plus one. If `maybe_limit` is `None` then all values in the client backend are
259/// deleted. This is potentially unsafe since it's an unbounded operation.
260///
261/// ## Cursors
262///
263/// The `maybe_cursor` parameter should be `None` for the first call to initial removal.
264/// If the resultant `maybe_cursor` is `Some`, then another call is required to complete the
265/// removal operation. This value must be passed in as the subsequent call's `maybe_cursor`
266/// parameter. If the resultant `maybe_cursor` is `None`, then the operation is complete and no
267/// items remain in storage provided that no items were added between the first calls and the
268/// final call.
269pub fn clear_storage_prefix(
270	module: &[u8],
271	item: &[u8],
272	hash: &[u8],
273	maybe_limit: Option<u32>,
274	maybe_cursor: Option<&[u8]>,
275) -> subsoil::io::MultiRemovalResults {
276	let mut key = vec![0u8; 32 + hash.len()];
277	let storage_prefix = storage_prefix(module, item);
278	key[0..32].copy_from_slice(&storage_prefix);
279	key[32..].copy_from_slice(hash);
280	topsoil_core::storage::unhashed::clear_prefix(&key, maybe_limit, maybe_cursor)
281}
282
283/// Take a particular item in storage by the `module`, the map's `item` name and the key `hash`.
284pub fn take_storage_item<K: Encode + Sized, T: Decode + Sized, H: StorageHasher>(
285	module: &[u8],
286	item: &[u8],
287	key: K,
288) -> Option<T> {
289	take_storage_value(module, item, key.using_encoded(H::hash).as_ref())
290}
291
292/// Move a storage from a pallet prefix to another pallet prefix.
293///
294/// Keys used in pallet storages always start with:
295/// `concat(twox_128(pallet_name), twox_128(storage_name))`.
296///
297/// This function will remove all value for which the key start with
298/// `concat(twox_128(old_pallet_name), twox_128(storage_name))` and insert them at the key with
299/// the start replaced by `concat(twox_128(new_pallet_name), twox_128(storage_name))`.
300///
301/// # Example
302///
303/// If a pallet named "my_example" has 2 storages named "Foo" and "Bar" and the pallet is renamed
304/// "my_new_example_name", a migration can be:
305/// ```
306/// # use topsoil_core::storage::migration::move_storage_from_pallet;
307/// # subsoil::io::TestExternalities::new_empty().execute_with(|| {
308/// move_storage_from_pallet(b"Foo", b"my_example", b"my_new_example_name");
309/// move_storage_from_pallet(b"Bar", b"my_example", b"my_new_example_name");
310/// # })
311/// ```
312pub fn move_storage_from_pallet(
313	storage_name: &[u8],
314	old_pallet_name: &[u8],
315	new_pallet_name: &[u8],
316) {
317	let new_prefix = storage_prefix(new_pallet_name, storage_name);
318	let old_prefix = storage_prefix(old_pallet_name, storage_name);
319
320	move_prefix(&old_prefix, &new_prefix);
321
322	if let Some(value) = unhashed::get_raw(&old_prefix) {
323		unhashed::put_raw(&new_prefix, &value);
324		unhashed::kill(&old_prefix);
325	}
326}
327
328/// Move all storages from a pallet prefix to another pallet prefix.
329///
330/// Keys used in pallet storages always start with:
331/// `concat(twox_128(pallet_name), twox_128(storage_name))`.
332///
333/// This function will remove all value for which the key start with `twox_128(old_pallet_name)`
334/// and insert them at the key with the start replaced by `twox_128(new_pallet_name)`.
335///
336/// NOTE: The value at the key `twox_128(old_pallet_name)` is not moved.
337///
338/// # Example
339///
340/// If a pallet named "my_example" has some storages and the pallet is renamed
341/// "my_new_example_name", a migration can be:
342/// ```
343/// # use topsoil_core::storage::migration::move_pallet;
344/// # subsoil::io::TestExternalities::new_empty().execute_with(|| {
345/// move_pallet(b"my_example", b"my_new_example_name");
346/// # })
347/// ```
348pub fn move_pallet(old_pallet_name: &[u8], new_pallet_name: &[u8]) {
349	move_prefix(&Twox128::hash(old_pallet_name), &Twox128::hash(new_pallet_name))
350}
351
352/// Move all `(key, value)` after some prefix to the another prefix
353///
354/// This function will remove all value for which the key start with `from_prefix`
355/// and insert them at the key with the start replaced by `to_prefix`.
356///
357/// NOTE: The value at the key `from_prefix` is not moved.
358pub fn move_prefix(from_prefix: &[u8], to_prefix: &[u8]) {
359	if from_prefix == to_prefix {
360		return;
361	}
362
363	let iter = PrefixIterator::<_> {
364		prefix: from_prefix.to_vec(),
365		previous_key: from_prefix.to_vec(),
366		drain: true,
367		closure: |key, value| Ok((key.to_vec(), value.to_vec())),
368		phantom: Default::default(),
369	};
370
371	for (key, value) in iter {
372		let full_key = [to_prefix, &key].concat();
373		unhashed::put_raw(&full_key, &value);
374	}
375}
376
377#[cfg(test)]
378mod tests {
379	use super::{
380		move_pallet, move_prefix, move_storage_from_pallet, storage_iter, storage_key_iter,
381	};
382	use crate::{
383		hash::StorageHasher,
384		pallet_prelude::{StorageMap, StorageValue, Twox128, Twox64Concat},
385	};
386	use subsoil::io::TestExternalities;
387
388	struct OldPalletStorageValuePrefix;
389	impl topsoil_core::traits::StorageInstance for OldPalletStorageValuePrefix {
390		const STORAGE_PREFIX: &'static str = "foo_value";
391		fn pallet_prefix() -> &'static str {
392			"my_old_pallet"
393		}
394	}
395	type OldStorageValue = StorageValue<OldPalletStorageValuePrefix, u32>;
396
397	struct OldPalletStorageMapPrefix;
398	impl topsoil_core::traits::StorageInstance for OldPalletStorageMapPrefix {
399		const STORAGE_PREFIX: &'static str = "foo_map";
400		fn pallet_prefix() -> &'static str {
401			"my_old_pallet"
402		}
403	}
404	type OldStorageMap = StorageMap<OldPalletStorageMapPrefix, Twox64Concat, u32, u32>;
405
406	struct NewPalletStorageValuePrefix;
407	impl topsoil_core::traits::StorageInstance for NewPalletStorageValuePrefix {
408		const STORAGE_PREFIX: &'static str = "foo_value";
409		fn pallet_prefix() -> &'static str {
410			"my_new_pallet"
411		}
412	}
413	type NewStorageValue = StorageValue<NewPalletStorageValuePrefix, u32>;
414
415	struct NewPalletStorageMapPrefix;
416	impl topsoil_core::traits::StorageInstance for NewPalletStorageMapPrefix {
417		const STORAGE_PREFIX: &'static str = "foo_map";
418		fn pallet_prefix() -> &'static str {
419			"my_new_pallet"
420		}
421	}
422	type NewStorageMap = StorageMap<NewPalletStorageMapPrefix, Twox64Concat, u32, u32>;
423
424	#[test]
425	fn test_move_prefix() {
426		TestExternalities::new_empty().execute_with(|| {
427			OldStorageValue::put(3);
428			OldStorageMap::insert(1, 2);
429			OldStorageMap::insert(3, 4);
430
431			move_prefix(&Twox128::hash(b"my_old_pallet"), &Twox128::hash(b"my_new_pallet"));
432
433			assert_eq!(OldStorageValue::get(), None);
434			assert_eq!(OldStorageMap::iter().collect::<Vec<_>>(), vec![]);
435			assert_eq!(NewStorageValue::get(), Some(3));
436			assert_eq!(NewStorageMap::iter().collect::<Vec<_>>(), vec![(1, 2), (3, 4)]);
437		})
438	}
439
440	#[test]
441	fn test_move_storage() {
442		TestExternalities::new_empty().execute_with(|| {
443			OldStorageValue::put(3);
444			OldStorageMap::insert(1, 2);
445			OldStorageMap::insert(3, 4);
446
447			move_storage_from_pallet(b"foo_map", b"my_old_pallet", b"my_new_pallet");
448
449			assert_eq!(OldStorageValue::get(), Some(3));
450			assert_eq!(OldStorageMap::iter().collect::<Vec<_>>(), vec![]);
451			assert_eq!(NewStorageValue::get(), None);
452			assert_eq!(NewStorageMap::iter().collect::<Vec<_>>(), vec![(1, 2), (3, 4)]);
453
454			move_storage_from_pallet(b"foo_value", b"my_old_pallet", b"my_new_pallet");
455
456			assert_eq!(OldStorageValue::get(), None);
457			assert_eq!(OldStorageMap::iter().collect::<Vec<_>>(), vec![]);
458			assert_eq!(NewStorageValue::get(), Some(3));
459			assert_eq!(NewStorageMap::iter().collect::<Vec<_>>(), vec![(1, 2), (3, 4)]);
460		})
461	}
462
463	#[test]
464	fn test_move_pallet() {
465		TestExternalities::new_empty().execute_with(|| {
466			OldStorageValue::put(3);
467			OldStorageMap::insert(1, 2);
468			OldStorageMap::insert(3, 4);
469
470			move_pallet(b"my_old_pallet", b"my_new_pallet");
471
472			assert_eq!(OldStorageValue::get(), None);
473			assert_eq!(OldStorageMap::iter().collect::<Vec<_>>(), vec![]);
474			assert_eq!(NewStorageValue::get(), Some(3));
475			assert_eq!(NewStorageMap::iter().collect::<Vec<_>>(), vec![(1, 2), (3, 4)]);
476		})
477	}
478
479	#[test]
480	fn test_storage_iter() {
481		TestExternalities::new_empty().execute_with(|| {
482			OldStorageValue::put(3);
483			OldStorageMap::insert(1, 2);
484			OldStorageMap::insert(3, 4);
485
486			assert_eq!(
487				storage_key_iter::<i32, i32, Twox64Concat>(b"my_old_pallet", b"foo_map")
488					.collect::<Vec<_>>(),
489				vec![(1, 2), (3, 4)],
490			);
491
492			assert_eq!(
493				storage_iter(b"my_old_pallet", b"foo_map")
494					.drain()
495					.map(|t| t.1)
496					.collect::<Vec<i32>>(),
497				vec![2, 4],
498			);
499			assert_eq!(OldStorageMap::iter().collect::<Vec<_>>(), vec![]);
500
501			// Empty because storage iterator skips over the entry under the first key
502			assert_eq!(storage_iter::<i32>(b"my_old_pallet", b"foo_value").drain().next(), None);
503			assert_eq!(OldStorageValue::get(), Some(3));
504		});
505	}
506}