referrerpolicy=no-referrer-when-downgrade

pallet_state_trie_migration/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Pallet State Trie Migration
19//!
20//! Reads and writes all keys and values in the entire state in a systematic way. This is useful for
21//! upgrading a chain to [`sp-core::StateVersion::V1`], where all keys need to be touched.
22//!
23//! ## Migration Types
24//!
25//! This pallet provides 2 ways to do this, each of which is suited for a particular use-case, and
26//! can be enabled independently.
27//!
28//! ### Auto migration
29//!
30//! This system will try and migrate all keys by continuously using `on_initialize`. It is only
31//! sensible for a relay chain or a solo chain, where going slightly over weight is not a problem.
32//! It can be configured so that the migration takes at most `n` items and tries to not go over `x`
33//! bytes, but the latter is not guaranteed.
34//!
35//! For example, if a chain contains keys of 1 byte size, the `on_initialize` could read up to `x -
36//! 1` bytes from `n` different keys, while the next key is suddenly `:code:`, and there is no way
37//! to bail out of this.
38//!
39//! ### Signed migration
40//!
41//! As a backup, the migration process can be set in motion via signed transactions that basically
42//! say in advance how many items and how many bytes they will consume, and pay for it as well. This
43//! can be a good safe alternative, if the former system is not desirable.
44//!
45//! The (minor) caveat of this approach is that we cannot know in advance how many bytes reading a
46//! certain number of keys will incur. To overcome this, the runtime needs to configure this pallet
47//! with a `SignedDepositPerItem`. This is the per-item deposit that the origin of the signed
48//! migration transactions need to have in their account (on top of the normal fee) and if the size
49//! witness data that they claim is incorrect, this deposit is slashed.
50//!
51//! ---
52//!
53//! Initially, this pallet does not contain any auto migration. They must be manually enabled by the
54//! `ControlOrigin`.
55
56#![cfg_attr(not(feature = "std"), no_std)]
57
58extern crate alloc;
59
60pub use pallet::*;
61pub mod weights;
62
63const LOG_TARGET: &str = "runtime::state-trie-migration";
64
65#[macro_export]
66macro_rules! log {
67	($level:tt, $patter:expr $(, $values:expr)* $(,)?) => {
68		log::$level!(
69			target: crate::LOG_TARGET,
70			concat!("[{:?}] 🤖 ", $patter), frame_system::Pallet::<T>::block_number() $(, $values)*
71		)
72	};
73}
74
75#[frame_support::pallet]
76pub mod pallet {
77
78	pub use crate::weights::WeightInfo;
79
80	use alloc::{vec, vec::Vec};
81	use core::ops::Deref;
82	use frame_support::{
83		dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo},
84		ensure,
85		pallet_prelude::*,
86		traits::{
87			fungible::{hold::Balanced, Inspect, InspectHold, Mutate, MutateHold},
88			tokens::{Fortitude, Precision},
89			Get,
90		},
91	};
92	use frame_system::{self, pallet_prelude::*};
93	use sp_core::{
94		hexdisplay::HexDisplay, storage::well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX,
95	};
96	use sp_runtime::{
97		self,
98		traits::{Saturating, Zero},
99	};
100
101	pub(crate) type BalanceOf<T> =
102		<<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
103
104	/// The progress of either the top or child keys.
105	#[derive(
106		CloneNoBound,
107		Encode,
108		Decode,
109		DecodeWithMemTracking,
110		scale_info::TypeInfo,
111		PartialEqNoBound,
112		EqNoBound,
113		MaxEncodedLen,
114	)]
115	#[scale_info(skip_type_params(MaxKeyLen))]
116	pub enum Progress<MaxKeyLen: Get<u32>> {
117		/// Yet to begin.
118		ToStart,
119		/// Ongoing, with the last key given.
120		LastKey(BoundedVec<u8, MaxKeyLen>),
121		/// All done.
122		Complete,
123	}
124
125	/// Convenience type for easier usage of [`Progress`].
126	pub type ProgressOf<T> = Progress<<T as Config>::MaxKeyLen>;
127
128	/// A migration task stored in state.
129	///
130	/// It tracks the last top and child keys read.
131	#[derive(
132		Clone,
133		Encode,
134		Decode,
135		DecodeWithMemTracking,
136		scale_info::TypeInfo,
137		PartialEq,
138		Eq,
139		MaxEncodedLen,
140	)]
141	#[scale_info(skip_type_params(T))]
142	pub struct MigrationTask<T: Config> {
143		/// The current top trie migration progress.
144		pub(crate) progress_top: ProgressOf<T>,
145		/// The current child trie migration progress.
146		///
147		/// If `ToStart`, no further top keys are processed until the child key migration is
148		/// `Complete`.
149		pub(crate) progress_child: ProgressOf<T>,
150
151		/// Dynamic counter for the number of items that we have processed in this execution from
152		/// the top trie.
153		///
154		/// It is not written to storage.
155		#[codec(skip)]
156		pub(crate) dyn_top_items: u32,
157		/// Dynamic counter for the number of items that we have processed in this execution from
158		/// any child trie.
159		///
160		/// It is not written to storage.
161		#[codec(skip)]
162		pub(crate) dyn_child_items: u32,
163
164		/// Dynamic counter for for the byte size of items that we have processed in this
165		/// execution.
166		///
167		/// It is not written to storage.
168		#[codec(skip)]
169		pub(crate) dyn_size: u32,
170
171		/// The total size of the migration, over all executions.
172		///
173		/// This only kept around for bookkeeping and debugging.
174		pub(crate) size: u32,
175		/// The total count of top keys in the migration, over all executions.
176		///
177		/// This only kept around for bookkeeping and debugging.
178		pub(crate) top_items: u32,
179		/// The total count of child keys in the migration, over all executions.
180		///
181		/// This only kept around for bookkeeping and debugging.
182		pub(crate) child_items: u32,
183
184		#[codec(skip)]
185		pub(crate) _ph: core::marker::PhantomData<T>,
186	}
187
188	impl<Size: Get<u32>> core::fmt::Debug for Progress<Size> {
189		fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
190			match self {
191				Progress::ToStart => f.write_str("To start"),
192				Progress::LastKey(key) => write!(f, "Last: {:?}", HexDisplay::from(key.deref())),
193				Progress::Complete => f.write_str("Complete"),
194			}
195		}
196	}
197
198	impl<T: Config> core::fmt::Debug for MigrationTask<T> {
199		fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
200			f.debug_struct("MigrationTask")
201				.field("top", &self.progress_top)
202				.field("child", &self.progress_child)
203				.field("dyn_top_items", &self.dyn_top_items)
204				.field("dyn_child_items", &self.dyn_child_items)
205				.field("dyn_size", &self.dyn_size)
206				.field("size", &self.size)
207				.field("top_items", &self.top_items)
208				.field("child_items", &self.child_items)
209				.finish()
210		}
211	}
212
213	impl<T: Config> Default for MigrationTask<T> {
214		fn default() -> Self {
215			Self {
216				progress_top: Progress::ToStart,
217				progress_child: Progress::ToStart,
218				dyn_child_items: Default::default(),
219				dyn_top_items: Default::default(),
220				dyn_size: Default::default(),
221				_ph: Default::default(),
222				size: Default::default(),
223				top_items: Default::default(),
224				child_items: Default::default(),
225			}
226		}
227	}
228
229	impl<T: Config> MigrationTask<T> {
230		/// Return true if the task is finished.
231		pub(crate) fn finished(&self) -> bool {
232			matches!(self.progress_top, Progress::Complete)
233		}
234
235		/// Check if there's any work left, or if we have exhausted the limits already.
236		fn exhausted(&self, limits: MigrationLimits) -> bool {
237			self.dyn_total_items() >= limits.item || self.dyn_size >= limits.size
238		}
239
240		/// get the total number of keys affected by the current task.
241		pub(crate) fn dyn_total_items(&self) -> u32 {
242			self.dyn_child_items.saturating_add(self.dyn_top_items)
243		}
244
245		/// Migrate keys until either of the given limits are exhausted, or if no more top keys
246		/// exist.
247		///
248		/// Note that this can return after the **first** migration tick that causes exhaustion,
249		/// specifically in the case of the `size` constrain. The reason for this is that before
250		/// reading a key, we simply cannot know how many bytes it is. In other words, this should
251		/// not be used in any environment where resources are strictly bounded (e.g. a parachain),
252		/// but it is acceptable otherwise (relay chain, offchain workers).
253		pub fn migrate_until_exhaustion(
254			&mut self,
255			limits: MigrationLimits,
256		) -> Result<(), Error<T>> {
257			log!(debug, "running migrations on top of {:?} until {:?}", self, limits);
258
259			if limits.item.is_zero() || limits.size.is_zero() {
260				// handle this minor edge case, else we would call `migrate_tick` at least once.
261				log!(warn, "limits are zero. stopping");
262				return Ok(());
263			}
264
265			while !self.exhausted(limits) && !self.finished() {
266				if let Err(e) = self.migrate_tick() {
267					log!(error, "migrate_until_exhaustion failed: {:?}", e);
268					return Err(e);
269				}
270			}
271
272			// accumulate dynamic data into the storage items.
273			self.size = self.size.saturating_add(self.dyn_size);
274			self.child_items = self.child_items.saturating_add(self.dyn_child_items);
275			self.top_items = self.top_items.saturating_add(self.dyn_top_items);
276			log!(debug, "finished with {:?}", self);
277			Ok(())
278		}
279
280		/// Migrate AT MOST ONE KEY. This can be either a top or a child key.
281		///
282		/// This function is *the* core of this entire pallet.
283		fn migrate_tick(&mut self) -> Result<(), Error<T>> {
284			match (&self.progress_top, &self.progress_child) {
285				(Progress::ToStart, _) => self.migrate_top(),
286				(Progress::LastKey(_), Progress::LastKey(_)) => {
287					// we're in the middle of doing work on a child tree.
288					self.migrate_child()
289				},
290				(Progress::LastKey(top_key), Progress::ToStart) => {
291					// 3. this is the root of a child key, and we are finishing all child-keys (and
292					// should call `migrate_top`).
293
294					// NOTE: this block is written intentionally to verbosely for easy of
295					// verification.
296					if !top_key.starts_with(DEFAULT_CHILD_STORAGE_KEY_PREFIX) {
297						// we continue the top key migrations.
298						// continue the top key migration
299						self.migrate_top()
300					} else {
301						// this is the root of a child key, and we start processing child keys (and
302						// should call `migrate_child`).
303						self.migrate_child()
304					}
305				},
306				(Progress::LastKey(_), Progress::Complete) => {
307					// we're done with migrating a child-root.
308					self.migrate_top()?;
309					self.progress_child = Progress::ToStart;
310					Ok(())
311				},
312				(Progress::Complete, _) => {
313					// nada
314					Ok(())
315				},
316			}
317		}
318
319		/// Migrate the current child key, setting it to its new value, if one exists.
320		///
321		/// It updates the dynamic counters.
322		fn migrate_child(&mut self) -> Result<(), Error<T>> {
323			use sp_io::default_child_storage as child_io;
324			let (maybe_current_child, child_root) = match (&self.progress_child, &self.progress_top)
325			{
326				(Progress::LastKey(last_child), Progress::LastKey(last_top)) => {
327					let child_root = Pallet::<T>::transform_child_key_or_halt(last_top);
328					let maybe_current_child: Option<BoundedVec<u8, T::MaxKeyLen>> =
329						if let Some(next) = child_io::next_key(child_root, last_child) {
330							Some(next.try_into().map_err(|_| Error::<T>::KeyTooLong)?)
331						} else {
332							None
333						};
334
335					(maybe_current_child, child_root)
336				},
337				(Progress::ToStart, Progress::LastKey(last_top)) => {
338					let child_root = Pallet::<T>::transform_child_key_or_halt(last_top);
339					// Start with the empty key as first key.
340					(Some(Default::default()), child_root)
341				},
342				_ => {
343					// defensive: there must be an ongoing top migration.
344					frame_support::defensive!("cannot migrate child key.");
345					return Ok(());
346				},
347			};
348
349			if let Some(current_child) = maybe_current_child.as_ref() {
350				let added_size = if let Some(data) = child_io::get(child_root, current_child) {
351					child_io::set(child_root, current_child, &data);
352					data.len() as u32
353				} else {
354					Zero::zero()
355				};
356				self.dyn_size = self.dyn_size.saturating_add(added_size);
357				self.dyn_child_items.saturating_inc();
358			}
359
360			log!(trace, "migrated a child key, next_child_key: {:?}", maybe_current_child);
361			self.progress_child = match maybe_current_child {
362				Some(last_child) => Progress::LastKey(last_child),
363				None => Progress::Complete,
364			};
365			Ok(())
366		}
367
368		/// Migrate the current top key, setting it to its new value, if one exists.
369		///
370		/// It updates the dynamic counters.
371		fn migrate_top(&mut self) -> Result<(), Error<T>> {
372			let maybe_current_top = match &self.progress_top {
373				Progress::LastKey(last_top) => {
374					let maybe_top: Option<BoundedVec<u8, T::MaxKeyLen>> =
375						if let Some(next) = sp_io::storage::next_key(last_top) {
376							Some(next.try_into().map_err(|_| Error::<T>::KeyTooLong)?)
377						} else {
378							None
379						};
380					maybe_top
381				},
382				// Start with the empty key as first key.
383				Progress::ToStart => Some(Default::default()),
384				Progress::Complete => {
385					// defensive: there must be an ongoing top migration.
386					frame_support::defensive!("cannot migrate top key.");
387					return Ok(());
388				},
389			};
390
391			if let Some(current_top) = maybe_current_top.as_ref() {
392				let added_size = if let Some(data) = sp_io::storage::get(current_top) {
393					sp_io::storage::set(current_top, &data);
394					data.len() as u32
395				} else {
396					Zero::zero()
397				};
398				self.dyn_size = self.dyn_size.saturating_add(added_size);
399				self.dyn_top_items.saturating_inc();
400			}
401
402			log!(trace, "migrated a top key, next_top_key = {:?}", maybe_current_top);
403			self.progress_top = match maybe_current_top {
404				Some(last_top) => Progress::LastKey(last_top),
405				None => Progress::Complete,
406			};
407			Ok(())
408		}
409	}
410
411	/// The limits of a migration.
412	#[derive(
413		Clone,
414		Copy,
415		Encode,
416		Decode,
417		DecodeWithMemTracking,
418		scale_info::TypeInfo,
419		Default,
420		Debug,
421		PartialEq,
422		Eq,
423		MaxEncodedLen,
424	)]
425	pub struct MigrationLimits {
426		/// The byte size limit.
427		pub size: u32,
428		/// The number of keys limit.
429		pub item: u32,
430	}
431
432	/// How a migration was computed.
433	#[derive(
434		Clone,
435		Copy,
436		Encode,
437		Decode,
438		DecodeWithMemTracking,
439		scale_info::TypeInfo,
440		Debug,
441		PartialEq,
442		Eq,
443	)]
444	pub enum MigrationCompute {
445		/// A signed origin triggered the migration.
446		Signed,
447		/// An automatic task triggered the migration.
448		Auto,
449	}
450
451	/// Inner events of this pallet.
452	#[pallet::event]
453	#[pallet::generate_deposit(pub(super) fn deposit_event)]
454	pub enum Event<T: Config> {
455		/// Given number of `(top, child)` keys were migrated respectively, with the given
456		/// `compute`.
457		Migrated { top: u32, child: u32, compute: MigrationCompute },
458		/// Some account got slashed by the given amount.
459		Slashed { who: T::AccountId, amount: BalanceOf<T> },
460		/// The auto migration task finished.
461		AutoMigrationFinished,
462		/// Migration got halted due to an error or miss-configuration.
463		Halted { error: Error<T> },
464	}
465
466	/// The outer Pallet struct.
467	#[pallet::pallet]
468	pub struct Pallet<T>(_);
469
470	/// Default implementations of [`DefaultConfig`], which can be used to implement [`Config`].
471	pub mod config_preludes {
472		use super::*;
473		use frame_support::derive_impl;
474
475		pub struct TestDefaultConfig;
476
477		#[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
478		impl frame_system::DefaultConfig for TestDefaultConfig {}
479
480		#[frame_support::register_default_impl(TestDefaultConfig)]
481		impl DefaultConfig for TestDefaultConfig {
482			#[inject_runtime_type]
483			type RuntimeEvent = ();
484			#[inject_runtime_type]
485			type RuntimeHoldReason = ();
486		}
487	}
488
489	/// The reason for this pallet placing a hold on funds.
490	#[pallet::composite_enum]
491	pub enum HoldReason {
492		/// The funds are held as a deposit for slashing.
493		#[codec(index = 0)]
494		SlashForMigrate,
495	}
496
497	/// Configurations of this pallet.
498	#[pallet::config(with_default)]
499	pub trait Config: frame_system::Config {
500		/// Origin that can control the configurations of this pallet.
501		#[pallet::no_default]
502		type ControlOrigin: EnsureOrigin<Self::RuntimeOrigin>;
503
504		/// Filter on which origin that trigger the manual migrations.
505		#[pallet::no_default]
506		type SignedFilter: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
507
508		/// The overarching event type.
509		#[pallet::no_default_bounds]
510		#[allow(deprecated)]
511		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
512
513		/// The currency provider type.
514		#[pallet::no_default]
515		type Currency: InspectHold<Self::AccountId>
516			+ Mutate<Self::AccountId>
517			+ MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>
518			+ Balanced<Self::AccountId>;
519
520		/// The overarching runtime hold reason.
521		#[pallet::no_default_bounds]
522		type RuntimeHoldReason: From<HoldReason>;
523
524		/// Maximal number of bytes that a key can have.
525		///
526		/// FRAME itself does not limit the key length.
527		/// The concrete value must therefore depend on your storage usage.
528		/// A [`frame_support::storage::StorageNMap`] for example can have an arbitrary number of
529		/// keys which are then hashed and concatenated, resulting in arbitrarily long keys.
530		///
531		/// Use the *state migration RPC* to retrieve the length of the longest key in your
532		/// storage: <https://github.com/paritytech/substrate/issues/11642>
533		///
534		/// The migration will halt with a `Halted` event if this value is too small.
535		/// Since there is no real penalty from over-estimating, it is advised to use a large
536		/// value. The default is 512 byte.
537		///
538		/// Some key lengths for reference:
539		/// - [`frame_support::storage::StorageValue`]: 32 byte
540		/// - [`frame_support::storage::StorageMap`]: 64 byte
541		/// - [`frame_support::storage::StorageDoubleMap`]: 96 byte
542		///
543		/// For more info see
544		/// <https://www.shawntabrizi.com/blog/substrate/querying-substrate-storage-via-rpc/>
545
546		#[pallet::constant]
547		#[pallet::no_default]
548		type MaxKeyLen: Get<u32>;
549
550		/// The amount of deposit collected per item in advance, for signed migrations.
551		///
552		/// This should reflect the average storage value size in the worse case.
553		#[pallet::no_default]
554		type SignedDepositPerItem: Get<BalanceOf<Self>>;
555
556		/// The base value of [`Config::SignedDepositPerItem`].
557		///
558		/// Final deposit is `items * SignedDepositPerItem + SignedDepositBase`.
559		#[pallet::no_default]
560		type SignedDepositBase: Get<BalanceOf<Self>>;
561
562		/// The weight information of this pallet.
563		#[pallet::no_default]
564		type WeightInfo: WeightInfo;
565	}
566
567	/// Migration progress.
568	///
569	/// This stores the snapshot of the last migrated keys. It can be set into motion and move
570	/// forward by any of the means provided by this pallet.
571	#[pallet::storage]
572	#[pallet::getter(fn migration_process)]
573	pub type MigrationProcess<T> = StorageValue<_, MigrationTask<T>, ValueQuery>;
574
575	/// The limits that are imposed on automatic migrations.
576	///
577	/// If set to None, then no automatic migration happens.
578	#[pallet::storage]
579	#[pallet::getter(fn auto_limits)]
580	pub type AutoLimits<T> = StorageValue<_, Option<MigrationLimits>, ValueQuery>;
581
582	/// The maximum limits that the signed migration could use.
583	///
584	/// If not set, no signed submission is allowed.
585	#[pallet::storage]
586	#[pallet::getter(fn signed_migration_max_limits)]
587	pub type SignedMigrationMaxLimits<T> = StorageValue<_, MigrationLimits, OptionQuery>;
588
589	#[pallet::error]
590	#[derive(Clone, PartialEq)]
591	pub enum Error<T> {
592		/// Max signed limits not respected.
593		MaxSignedLimits,
594		/// A key was longer than the configured maximum.
595		///
596		/// This means that the migration halted at the current [`Progress`] and
597		/// can be resumed with a larger [`crate::Config::MaxKeyLen`] value.
598		/// Retrying with the same [`crate::Config::MaxKeyLen`] value will not work.
599		/// The value should only be increased to avoid a storage migration for the currently
600		/// stored [`crate::Progress::LastKey`].
601		KeyTooLong,
602		/// submitter does not have enough funds.
603		NotEnoughFunds,
604		/// Bad witness data provided.
605		BadWitness,
606		/// Signed migration is not allowed because the maximum limit is not set yet.
607		SignedMigrationNotAllowed,
608		/// Bad child root provided.
609		BadChildRoot,
610	}
611
612	#[pallet::call]
613	impl<T: Config> Pallet<T> {
614		/// Control the automatic migration.
615		///
616		/// The dispatch origin of this call must be [`Config::ControlOrigin`].
617		#[pallet::call_index(0)]
618		#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
619		pub fn control_auto_migration(
620			origin: OriginFor<T>,
621			maybe_config: Option<MigrationLimits>,
622		) -> DispatchResult {
623			T::ControlOrigin::ensure_origin(origin)?;
624			AutoLimits::<T>::put(maybe_config);
625			Ok(())
626		}
627
628		/// Continue the migration for the given `limits`.
629		///
630		/// The dispatch origin of this call can be any signed account.
631		///
632		/// This transaction has NO MONETARY INCENTIVES. calling it will not reward anyone. Albeit,
633		/// Upon successful execution, the transaction fee is returned.
634		///
635		/// The (potentially over-estimated) of the byte length of all the data read must be
636		/// provided for up-front fee-payment and weighing. In essence, the caller is guaranteeing
637		/// that executing the current `MigrationTask` with the given `limits` will not exceed
638		/// `real_size_upper` bytes of read data.
639		///
640		/// The `witness_task` is merely a helper to prevent the caller from being slashed or
641		/// generally trigger a migration that they do not intend. This parameter is just a message
642		/// from caller, saying that they believed `witness_task` was the last state of the
643		/// migration, and they only wish for their transaction to do anything, if this assumption
644		/// holds. In case `witness_task` does not match, the transaction fails.
645		///
646		/// Based on the documentation of [`MigrationTask::migrate_until_exhaustion`], the
647		/// recommended way of doing this is to pass a `limit` that only bounds `count`, as the
648		/// `size` limit can always be overwritten.
649		#[pallet::call_index(1)]
650		#[pallet::weight(
651			// the migration process
652			Pallet::<T>::dynamic_weight(limits.item, * real_size_upper)
653			// rest of the operations, like deposit etc.
654			+ T::WeightInfo::continue_migrate()
655		)]
656		pub fn continue_migrate(
657			origin: OriginFor<T>,
658			limits: MigrationLimits,
659			real_size_upper: u32,
660			witness_task: MigrationTask<T>,
661		) -> DispatchResultWithPostInfo {
662			let who = T::SignedFilter::ensure_origin(origin)?;
663
664			let max_limits =
665				Self::signed_migration_max_limits().ok_or(Error::<T>::SignedMigrationNotAllowed)?;
666			ensure!(
667				limits.size <= max_limits.size && limits.item <= max_limits.item,
668				Error::<T>::MaxSignedLimits,
669			);
670
671			// ensure they can pay more than the fee.
672			let deposit = Self::calculate_deposit_for(limits.item);
673			ensure!(
674				T::Currency::can_hold(&HoldReason::SlashForMigrate.into(), &who, deposit),
675				Error::<T>::NotEnoughFunds
676			);
677
678			let mut task = Self::migration_process();
679			ensure!(
680				task == witness_task,
681				DispatchErrorWithPostInfo {
682					error: Error::<T>::BadWitness.into(),
683					post_info: PostDispatchInfo {
684						actual_weight: Some(T::WeightInfo::continue_migrate_wrong_witness()),
685						pays_fee: Pays::Yes
686					}
687				}
688			);
689			let migration = task.migrate_until_exhaustion(limits);
690
691			// ensure that the migration witness data was correct.
692			if real_size_upper < task.dyn_size {
693				Self::slash(who, deposit)?;
694				return Ok(().into());
695			}
696
697			Self::deposit_event(Event::<T>::Migrated {
698				top: task.dyn_top_items,
699				child: task.dyn_child_items,
700				compute: MigrationCompute::Signed,
701			});
702
703			// refund and correct the weight.
704			let actual_weight = Some(
705				Pallet::<T>::dynamic_weight(limits.item, task.dyn_size)
706					.saturating_add(T::WeightInfo::continue_migrate()),
707			);
708
709			MigrationProcess::<T>::put(task);
710			let post_info = PostDispatchInfo { actual_weight, pays_fee: Pays::No };
711			if let Err(error) = migration {
712				Self::halt(error);
713			}
714			Ok(post_info)
715		}
716
717		/// Migrate the list of top keys by iterating each of them one by one.
718		///
719		/// This does not affect the global migration process tracker ([`MigrationProcess`]), and
720		/// should only be used in case any keys are leftover due to a bug.
721		#[pallet::call_index(2)]
722		#[pallet::weight(
723			T::WeightInfo::migrate_custom_top_success()
724				.max(T::WeightInfo::migrate_custom_top_fail())
725			.saturating_add(
726				Pallet::<T>::dynamic_weight(keys.len() as u32, *witness_size)
727			)
728		)]
729		pub fn migrate_custom_top(
730			origin: OriginFor<T>,
731			keys: Vec<Vec<u8>>,
732			witness_size: u32,
733		) -> DispatchResultWithPostInfo {
734			let who = T::SignedFilter::ensure_origin(origin)?;
735
736			// ensure they can pay more than the fee.
737			let deposit = Self::calculate_deposit_for(keys.len() as u32);
738			ensure!(
739				T::Currency::can_hold(&HoldReason::SlashForMigrate.into(), &who, deposit),
740				Error::<T>::NotEnoughFunds
741			);
742
743			let mut dyn_size = 0u32;
744			for key in &keys {
745				if let Some(data) = sp_io::storage::get(key) {
746					dyn_size = dyn_size.saturating_add(data.len() as u32);
747					sp_io::storage::set(key, &data);
748				}
749			}
750
751			if dyn_size > witness_size {
752				Self::slash(who, deposit)?;
753				Ok(().into())
754			} else {
755				Self::deposit_event(Event::<T>::Migrated {
756					top: keys.len() as u32,
757					child: 0,
758					compute: MigrationCompute::Signed,
759				});
760				Ok(PostDispatchInfo {
761					actual_weight: Some(
762						T::WeightInfo::migrate_custom_top_success().saturating_add(
763							Pallet::<T>::dynamic_weight(keys.len() as u32, dyn_size),
764						),
765					),
766					pays_fee: Pays::Yes,
767				})
768			}
769		}
770
771		/// Migrate the list of child keys by iterating each of them one by one.
772		///
773		/// All of the given child keys must be present under one `child_root`.
774		///
775		/// This does not affect the global migration process tracker ([`MigrationProcess`]), and
776		/// should only be used in case any keys are leftover due to a bug.
777		#[pallet::call_index(3)]
778		#[pallet::weight(
779			T::WeightInfo::migrate_custom_child_success()
780				.max(T::WeightInfo::migrate_custom_child_fail())
781			.saturating_add(
782				Pallet::<T>::dynamic_weight(child_keys.len() as u32, *total_size)
783			)
784		)]
785		pub fn migrate_custom_child(
786			origin: OriginFor<T>,
787			root: Vec<u8>,
788			child_keys: Vec<Vec<u8>>,
789			total_size: u32,
790		) -> DispatchResultWithPostInfo {
791			use sp_io::default_child_storage as child_io;
792			let who = T::SignedFilter::ensure_origin(origin)?;
793
794			// ensure they can pay more than the fee.
795			let deposit = Self::calculate_deposit_for(child_keys.len() as u32);
796			ensure!(
797				T::Currency::can_hold(&HoldReason::SlashForMigrate.into(), &who, deposit),
798				Error::<T>::NotEnoughFunds
799			);
800
801			let mut dyn_size = 0u32;
802			let transformed_child_key = Self::transform_child_key(&root).ok_or("bad child key")?;
803			for child_key in &child_keys {
804				if let Some(data) = child_io::get(transformed_child_key, child_key) {
805					dyn_size = dyn_size.saturating_add(data.len() as u32);
806					child_io::set(transformed_child_key, child_key, &data);
807				}
808			}
809
810			if dyn_size != total_size {
811				Self::slash(who, deposit)?;
812				Ok(PostDispatchInfo {
813					actual_weight: Some(T::WeightInfo::migrate_custom_child_fail()),
814					pays_fee: Pays::Yes,
815				})
816			} else {
817				Self::deposit_event(Event::<T>::Migrated {
818					top: 0,
819					child: child_keys.len() as u32,
820					compute: MigrationCompute::Signed,
821				});
822				Ok(PostDispatchInfo {
823					actual_weight: Some(
824						T::WeightInfo::migrate_custom_child_success().saturating_add(
825							Pallet::<T>::dynamic_weight(child_keys.len() as u32, total_size),
826						),
827					),
828					pays_fee: Pays::Yes,
829				})
830			}
831		}
832
833		/// Set the maximum limit of the signed migration.
834		#[pallet::call_index(4)]
835		#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
836		pub fn set_signed_max_limits(
837			origin: OriginFor<T>,
838			limits: MigrationLimits,
839		) -> DispatchResult {
840			T::ControlOrigin::ensure_origin(origin)?;
841			SignedMigrationMaxLimits::<T>::put(limits);
842			Ok(())
843		}
844
845		/// Forcefully set the progress the running migration.
846		///
847		/// This is only useful in one case: the next key to migrate is too big to be migrated with
848		/// a signed account, in a parachain context, and we simply want to skip it. A reasonable
849		/// example of this would be `:code:`, which is both very expensive to migrate, and commonly
850		/// used, so probably it is already migrated.
851		///
852		/// In case you mess things up, you can also, in principle, use this to reset the migration
853		/// process.
854		#[pallet::call_index(5)]
855		#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
856		pub fn force_set_progress(
857			origin: OriginFor<T>,
858			progress_top: ProgressOf<T>,
859			progress_child: ProgressOf<T>,
860		) -> DispatchResult {
861			T::ControlOrigin::ensure_origin(origin)?;
862			MigrationProcess::<T>::mutate(|task| {
863				task.progress_top = progress_top;
864				task.progress_child = progress_child;
865			});
866			Ok(())
867		}
868	}
869
870	#[pallet::hooks]
871	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
872		fn on_initialize(_: BlockNumberFor<T>) -> Weight {
873			if let Some(limits) = Self::auto_limits() {
874				let mut task = Self::migration_process();
875				if let Err(e) = task.migrate_until_exhaustion(limits) {
876					Self::halt(e);
877				}
878				let weight = Self::dynamic_weight(task.dyn_total_items(), task.dyn_size);
879
880				log!(
881					info,
882					"migrated {} top keys, {} child keys, and a total of {} bytes.",
883					task.dyn_top_items,
884					task.dyn_child_items,
885					task.dyn_size,
886				);
887
888				if task.finished() {
889					Self::deposit_event(Event::<T>::AutoMigrationFinished);
890					AutoLimits::<T>::kill();
891				} else {
892					Self::deposit_event(Event::<T>::Migrated {
893						top: task.dyn_top_items,
894						child: task.dyn_child_items,
895						compute: MigrationCompute::Auto,
896					});
897				}
898
899				MigrationProcess::<T>::put(task);
900
901				weight
902			} else {
903				T::DbWeight::get().reads(1)
904			}
905		}
906	}
907
908	impl<T: Config> Pallet<T> {
909		/// The real weight of a migration of the given number of `items` with total `size`.
910		fn dynamic_weight(items: u32, size: u32) -> frame_support::pallet_prelude::Weight {
911			let items = items as u64;
912			<T as frame_system::Config>::DbWeight::get()
913				.reads_writes(1, 1)
914				.saturating_mul(items)
915				// we assume that the read/write per-byte weight is the same for child and top tree.
916				.saturating_add(T::WeightInfo::process_top_key(size))
917		}
918
919		/// Put a stop to all ongoing migrations and logs an error.
920		fn halt(error: Error<T>) {
921			log!(error, "migration halted due to: {:?}", error);
922			AutoLimits::<T>::kill();
923			Self::deposit_event(Event::<T>::Halted { error });
924		}
925
926		/// Convert a child root key, aka. "Child-bearing top key" into the proper format.
927		fn transform_child_key(root: &Vec<u8>) -> Option<&[u8]> {
928			use sp_core::storage::{ChildType, PrefixedStorageKey};
929			match ChildType::from_prefixed_key(PrefixedStorageKey::new_ref(root)) {
930				Some((ChildType::ParentKeyId, root)) => Some(root),
931				_ => None,
932			}
933		}
934
935		/// Same as [`child_io_key`], and it halts the auto/unsigned migrations if a bad child root
936		/// is used.
937		///
938		/// This should be used when we are sure that `root` is a correct default child root.
939		fn transform_child_key_or_halt(root: &Vec<u8>) -> &[u8] {
940			let key = Self::transform_child_key(root);
941			if key.is_none() {
942				Self::halt(Error::<T>::BadChildRoot);
943			}
944			key.unwrap_or_default()
945		}
946
947		/// Convert a child root to be in the default child-tree.
948		#[cfg(any(test, feature = "runtime-benchmarks"))]
949		pub(crate) fn childify(root: &'static str) -> Vec<u8> {
950			let mut string = DEFAULT_CHILD_STORAGE_KEY_PREFIX.to_vec();
951			string.extend_from_slice(root.as_ref());
952			string
953		}
954
955		/// Calculate the deposit required for migrating a specific number of keys.
956		pub(crate) fn calculate_deposit_for(keys_count: u32) -> BalanceOf<T> {
957			T::SignedDepositBase::get()
958				.saturating_add(T::SignedDepositPerItem::get().saturating_mul(keys_count.into()))
959		}
960
961		/// Slash an account for migration.
962		fn slash(who: T::AccountId, amount: BalanceOf<T>) -> Result<(), DispatchError> {
963			T::Currency::hold(&HoldReason::SlashForMigrate.into(), &who, amount)?;
964			// let the imbalance burn.
965			let _burned = T::Currency::burn_all_held(
966				&HoldReason::SlashForMigrate.into(),
967				&who,
968				Precision::BestEffort,
969				Fortitude::Force,
970			)?;
971			debug_assert!(amount.saturating_sub(_burned).is_zero());
972			Self::deposit_event(Event::<T>::Slashed { who, amount });
973			Ok(())
974		}
975	}
976}
977
978#[cfg(feature = "runtime-benchmarks")]
979mod benchmarks {
980	use super::{pallet::Pallet as StateTrieMigration, *};
981	use alloc::vec;
982	use frame_benchmarking::v2::*;
983	use frame_support::traits::fungible::{Inspect, Mutate};
984
985	// The size of the key seemingly makes no difference in the read/write time, so we make it
986	// constant.
987	const KEY: &[u8] = b"key";
988
989	fn set_balance_for_deposit<T: Config>(caller: &T::AccountId, item: u32) -> BalanceOf<T> {
990		let deposit = StateTrieMigration::<T>::calculate_deposit_for(item);
991		let stash = T::Currency::minimum_balance() * BalanceOf::<T>::from(1000u32) + deposit;
992		T::Currency::set_balance(caller, stash);
993		stash
994	}
995
996	#[benchmarks]
997	mod inner_benchmarks {
998		use super::*;
999
1000		#[benchmark]
1001		fn continue_migrate() -> Result<(), BenchmarkError> {
1002			// note that this benchmark should migrate nothing, as we only want the overhead weight
1003			// of the bookkeeping, and the migration cost itself is noted via the `dynamic_weight`
1004			// function.
1005			let null = MigrationLimits::default();
1006			let caller = frame_benchmarking::whitelisted_caller();
1007			let stash = set_balance_for_deposit::<T>(&caller, null.item);
1008			// Allow signed migrations.
1009			SignedMigrationMaxLimits::<T>::put(MigrationLimits { size: 1024, item: 5 });
1010
1011			#[extrinsic_call]
1012			_(
1013				frame_system::RawOrigin::Signed(caller.clone()),
1014				null,
1015				0,
1016				StateTrieMigration::<T>::migration_process(),
1017			);
1018
1019			assert_eq!(StateTrieMigration::<T>::migration_process(), Default::default());
1020			assert_eq!(T::Currency::balance(&caller), stash);
1021
1022			Ok(())
1023		}
1024
1025		#[benchmark]
1026		fn continue_migrate_wrong_witness() -> Result<(), BenchmarkError> {
1027			let null = MigrationLimits::default();
1028			let caller = frame_benchmarking::whitelisted_caller();
1029			let bad_witness = MigrationTask {
1030				progress_top: Progress::LastKey(vec![1u8].try_into().unwrap()),
1031				..Default::default()
1032			};
1033			#[block]
1034			{
1035				assert!(StateTrieMigration::<T>::continue_migrate(
1036					frame_system::RawOrigin::Signed(caller).into(),
1037					null,
1038					0,
1039					bad_witness,
1040				)
1041				.is_err());
1042			}
1043
1044			assert_eq!(StateTrieMigration::<T>::migration_process(), Default::default());
1045
1046			Ok(())
1047		}
1048
1049		#[benchmark]
1050		fn migrate_custom_top_success() -> Result<(), BenchmarkError> {
1051			let null = MigrationLimits::default();
1052			let caller: T::AccountId = frame_benchmarking::whitelisted_caller();
1053			let stash = set_balance_for_deposit::<T>(&caller, null.item);
1054			#[extrinsic_call]
1055			migrate_custom_top(
1056				frame_system::RawOrigin::Signed(caller.clone()),
1057				Default::default(),
1058				0,
1059			);
1060
1061			assert_eq!(StateTrieMigration::<T>::migration_process(), Default::default());
1062			assert_eq!(T::Currency::balance(&caller), stash);
1063			Ok(())
1064		}
1065
1066		#[benchmark]
1067		fn migrate_custom_top_fail() -> Result<(), BenchmarkError> {
1068			let null = MigrationLimits::default();
1069			let caller: T::AccountId = frame_benchmarking::whitelisted_caller();
1070			let stash = set_balance_for_deposit::<T>(&caller, null.item);
1071			// for tests, we need to make sure there is _something_ in storage that is being
1072			// migrated.
1073			sp_io::storage::set(b"foo", vec![1u8; 33].as_ref());
1074			#[block]
1075			{
1076				assert!(StateTrieMigration::<T>::migrate_custom_top(
1077					frame_system::RawOrigin::Signed(caller.clone()).into(),
1078					vec![b"foo".to_vec()],
1079					1,
1080				)
1081				.is_ok());
1082
1083				frame_system::Pallet::<T>::assert_last_event(
1084					<T as Config>::RuntimeEvent::from(crate::Event::Slashed {
1085						who: caller.clone(),
1086						amount: StateTrieMigration::<T>::calculate_deposit_for(1u32),
1087					})
1088					.into(),
1089				);
1090			}
1091
1092			assert_eq!(StateTrieMigration::<T>::migration_process(), Default::default());
1093			// must have gotten slashed
1094			assert!(T::Currency::balance(&caller) < stash);
1095
1096			Ok(())
1097		}
1098
1099		#[benchmark]
1100		fn migrate_custom_child_success() -> Result<(), BenchmarkError> {
1101			let caller: T::AccountId = frame_benchmarking::whitelisted_caller();
1102			let stash = set_balance_for_deposit::<T>(&caller, 0);
1103
1104			#[extrinsic_call]
1105			migrate_custom_child(
1106				frame_system::RawOrigin::Signed(caller.clone()),
1107				StateTrieMigration::<T>::childify(Default::default()),
1108				Default::default(),
1109				0,
1110			);
1111
1112			assert_eq!(StateTrieMigration::<T>::migration_process(), Default::default());
1113			assert_eq!(T::Currency::balance(&caller), stash);
1114
1115			Ok(())
1116		}
1117
1118		#[benchmark]
1119		fn migrate_custom_child_fail() -> Result<(), BenchmarkError> {
1120			let caller: T::AccountId = frame_benchmarking::whitelisted_caller();
1121			let stash = set_balance_for_deposit::<T>(&caller, 1);
1122			// for tests, we need to make sure there is _something_ in storage that is being
1123			// migrated.
1124			sp_io::default_child_storage::set(b"top", b"foo", vec![1u8; 33].as_ref());
1125
1126			#[block]
1127			{
1128				assert!(StateTrieMigration::<T>::migrate_custom_child(
1129					frame_system::RawOrigin::Signed(caller.clone()).into(),
1130					StateTrieMigration::<T>::childify("top"),
1131					vec![b"foo".to_vec()],
1132					1,
1133				)
1134				.is_ok());
1135			}
1136			assert_eq!(StateTrieMigration::<T>::migration_process(), Default::default());
1137			// must have gotten slashed
1138			assert!(T::Currency::balance(&caller) < stash);
1139			Ok(())
1140		}
1141
1142		#[benchmark]
1143		fn process_top_key(v: Linear<1, { 4 * 1024 * 1024 }>) -> Result<(), BenchmarkError> {
1144			let value = alloc::vec![1u8; v as usize];
1145			sp_io::storage::set(KEY, &value);
1146			#[block]
1147			{
1148				let data = sp_io::storage::get(KEY).unwrap();
1149				sp_io::storage::set(KEY, &data);
1150				let _next = sp_io::storage::next_key(KEY);
1151				assert_eq!(data, value);
1152			}
1153
1154			Ok(())
1155		}
1156
1157		impl_benchmark_test_suite!(
1158			StateTrieMigration,
1159			crate::mock::new_test_ext(sp_runtime::StateVersion::V0, true, None, None),
1160			crate::mock::Test
1161		);
1162	}
1163}
1164
1165#[cfg(test)]
1166mod mock {
1167	use super::*;
1168	use crate as pallet_state_trie_migration;
1169	use alloc::{vec, vec::Vec};
1170	use frame_support::{derive_impl, parameter_types, traits::Hooks, weights::Weight};
1171	use frame_system::{EnsureRoot, EnsureSigned};
1172	use sp_core::{
1173		storage::{ChildInfo, StateVersion},
1174		H256,
1175	};
1176	use sp_runtime::{traits::Header as _, BuildStorage, StorageChild};
1177
1178	type Block = frame_system::mocking::MockBlockU32<Test>;
1179
1180	// Configure a mock runtime to test the pallet.
1181	frame_support::construct_runtime!(
1182		pub enum Test
1183		{
1184			System: frame_system,
1185			Balances: pallet_balances,
1186			StateTrieMigration: pallet_state_trie_migration,
1187		}
1188	);
1189
1190	parameter_types! {
1191		pub const SS58Prefix: u8 = 42;
1192	}
1193
1194	#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
1195	impl frame_system::Config for Test {
1196		type Block = Block;
1197		type AccountData = pallet_balances::AccountData<u64>;
1198	}
1199
1200	parameter_types! {
1201		pub const SignedDepositPerItem: u64 = 1;
1202		pub const SignedDepositBase: u64 = 5;
1203		pub const MigrationMaxKeyLen: u32 = 512;
1204	}
1205
1206	#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
1207	impl pallet_balances::Config for Test {
1208		type ReserveIdentifier = [u8; 8];
1209		type AccountStore = System;
1210	}
1211
1212	/// Test only Weights for state migration.
1213	pub struct StateMigrationTestWeight;
1214
1215	impl WeightInfo for StateMigrationTestWeight {
1216		fn process_top_key(_: u32) -> Weight {
1217			Weight::from_parts(1000000, 0)
1218		}
1219		fn continue_migrate() -> Weight {
1220			Weight::from_parts(1000000, 0)
1221		}
1222		fn continue_migrate_wrong_witness() -> Weight {
1223			Weight::from_parts(1000000, 0)
1224		}
1225		fn migrate_custom_top_fail() -> Weight {
1226			Weight::from_parts(1000000, 0)
1227		}
1228		fn migrate_custom_top_success() -> Weight {
1229			Weight::from_parts(1000000, 0)
1230		}
1231		fn migrate_custom_child_fail() -> Weight {
1232			Weight::from_parts(1000000, 0)
1233		}
1234		fn migrate_custom_child_success() -> Weight {
1235			Weight::from_parts(1000000, 0)
1236		}
1237	}
1238
1239	#[derive_impl(super::config_preludes::TestDefaultConfig)]
1240	impl pallet_state_trie_migration::Config for Test {
1241		type ControlOrigin = EnsureRoot<u64>;
1242		type Currency = Balances;
1243		type MaxKeyLen = MigrationMaxKeyLen;
1244		type SignedDepositPerItem = SignedDepositPerItem;
1245		type SignedDepositBase = SignedDepositBase;
1246		type SignedFilter = EnsureSigned<Self::AccountId>;
1247		type WeightInfo = StateMigrationTestWeight;
1248	}
1249
1250	pub fn new_test_ext(
1251		version: StateVersion,
1252		with_pallets: bool,
1253		custom_keys: Option<Vec<(Vec<u8>, Vec<u8>)>>,
1254		custom_child: Option<Vec<(Vec<u8>, Vec<u8>, Vec<u8>)>>,
1255	) -> sp_io::TestExternalities {
1256		let minimum_size = sp_core::storage::TRIE_VALUE_NODE_THRESHOLD as usize + 1;
1257		let mut custom_storage = sp_core::storage::Storage {
1258			top: vec![
1259				(b"key1".to_vec(), vec![1u8; minimum_size + 1]), // 6b657931
1260				(b"key2".to_vec(), vec![1u8; minimum_size + 2]), // 6b657931
1261				(b"key3".to_vec(), vec![1u8; minimum_size + 3]), // 6b657931
1262				(b"key4".to_vec(), vec![1u8; minimum_size + 4]), // 6b657931
1263				(b"key5".to_vec(), vec![1u8; minimum_size + 5]), // 6b657932
1264				(b"key6".to_vec(), vec![1u8; minimum_size + 6]), // 6b657934
1265				(b"key7".to_vec(), vec![1u8; minimum_size + 7]), // 6b657934
1266				(b"key8".to_vec(), vec![1u8; minimum_size + 8]), // 6b657934
1267				(b"key9".to_vec(), vec![1u8; minimum_size + 9]), // 6b657934
1268				(b"CODE".to_vec(), vec![1u8; minimum_size + 100]), // 434f4445
1269			]
1270			.into_iter()
1271			.chain(custom_keys.unwrap_or_default())
1272			.collect(),
1273			children_default: vec![
1274				(
1275					b"chk1".to_vec(), // 63686b31
1276					StorageChild {
1277						data: vec![
1278							(b"key1".to_vec(), vec![1u8; 55]),
1279							(b"key2".to_vec(), vec![2u8; 66]),
1280						]
1281						.into_iter()
1282						.collect(),
1283						child_info: ChildInfo::new_default(b"chk1"),
1284					},
1285				),
1286				(
1287					b"chk2".to_vec(),
1288					StorageChild {
1289						data: vec![
1290							(b"key1".to_vec(), vec![1u8; 54]),
1291							(b"key2".to_vec(), vec![2u8; 64]),
1292						]
1293						.into_iter()
1294						.collect(),
1295						child_info: ChildInfo::new_default(b"chk2"),
1296					},
1297				),
1298			]
1299			.into_iter()
1300			.chain(
1301				custom_child
1302					.unwrap_or_default()
1303					.into_iter()
1304					.map(|(r, k, v)| {
1305						(
1306							r.clone(),
1307							StorageChild {
1308								data: vec![(k, v)].into_iter().collect(),
1309								child_info: ChildInfo::new_default(&r),
1310							},
1311						)
1312					})
1313					.collect::<Vec<_>>(),
1314			)
1315			.collect(),
1316		};
1317
1318		if with_pallets {
1319			frame_system::GenesisConfig::<Test>::default()
1320				.assimilate_storage(&mut custom_storage)
1321				.unwrap();
1322			pallet_balances::GenesisConfig::<Test> {
1323				balances: vec![(1, 1000)],
1324				..Default::default()
1325			}
1326			.assimilate_storage(&mut custom_storage)
1327			.unwrap();
1328		}
1329
1330		sp_tracing::try_init_simple();
1331		(custom_storage, version).into()
1332	}
1333
1334	pub(crate) fn run_to_block(n: u32) -> (H256, Weight) {
1335		let mut root = Default::default();
1336		let mut weight_sum = Weight::zero();
1337
1338		log::trace!(target: LOG_TARGET, "running from {:?} to {:?}", System::block_number(), n);
1339
1340		System::run_to_block_with::<AllPalletsWithSystem>(
1341			n,
1342			frame_system::RunToBlockHooks::default().after_initialize(|bn| {
1343				weight_sum += StateTrieMigration::on_initialize(bn);
1344				root = *System::finalize().state_root();
1345			}),
1346		);
1347
1348		(root, weight_sum)
1349	}
1350}
1351
1352#[cfg(test)]
1353mod test {
1354	use super::{mock::*, *};
1355	use frame_support::assert_ok;
1356	use sp_runtime::{bounded_vec, traits::Bounded, StateVersion};
1357
1358	#[test]
1359	fn fails_if_no_migration() {
1360		let mut ext = new_test_ext(StateVersion::V0, false, None, None);
1361		let root1 = ext.execute_with(|| run_to_block(30).0);
1362
1363		let mut ext2 = new_test_ext(StateVersion::V1, false, None, None);
1364		let root2 = ext2.execute_with(|| run_to_block(30).0);
1365
1366		// these two roots should not be the same.
1367		assert_ne!(root1, root2);
1368	}
1369
1370	#[test]
1371	fn halts_if_top_key_too_long() {
1372		let bad_key = vec![1u8; MigrationMaxKeyLen::get() as usize + 1];
1373		let bad_top_keys = vec![(bad_key.clone(), vec![])];
1374
1375		new_test_ext(StateVersion::V0, true, Some(bad_top_keys), None).execute_with(|| {
1376			System::set_block_number(1);
1377			assert_eq!(MigrationProcess::<Test>::get(), Default::default());
1378
1379			// Allow signed migrations.
1380			SignedMigrationMaxLimits::<Test>::put(MigrationLimits { size: 1 << 20, item: 50 });
1381
1382			// fails if the top key is too long.
1383			frame_support::assert_ok!(StateTrieMigration::continue_migrate(
1384				RuntimeOrigin::signed(1),
1385				MigrationLimits { item: 50, size: 1 << 20 },
1386				Bounded::max_value(),
1387				MigrationProcess::<Test>::get()
1388			),);
1389			// The auto migration halted.
1390			System::assert_last_event(
1391				crate::Event::Halted { error: Error::<Test>::KeyTooLong }.into(),
1392			);
1393			// Limits are killed.
1394			assert!(AutoLimits::<Test>::get().is_none());
1395
1396			// Calling `migrate_until_exhaustion` also fails.
1397			let mut task = StateTrieMigration::migration_process();
1398			let result = task.migrate_until_exhaustion(
1399				StateTrieMigration::signed_migration_max_limits().unwrap(),
1400			);
1401			assert!(result.is_err());
1402		});
1403	}
1404
1405	#[test]
1406	fn halts_if_child_key_too_long() {
1407		let bad_key = vec![1u8; MigrationMaxKeyLen::get() as usize + 1];
1408		let bad_child_keys = vec![(bad_key.clone(), vec![], vec![])];
1409
1410		new_test_ext(StateVersion::V0, true, None, Some(bad_child_keys)).execute_with(|| {
1411			System::set_block_number(1);
1412			assert_eq!(MigrationProcess::<Test>::get(), Default::default());
1413
1414			// Allow signed migrations.
1415			SignedMigrationMaxLimits::<Test>::put(MigrationLimits { size: 1 << 20, item: 50 });
1416
1417			// fails if the top key is too long.
1418			frame_support::assert_ok!(StateTrieMigration::continue_migrate(
1419				RuntimeOrigin::signed(1),
1420				MigrationLimits { item: 50, size: 1 << 20 },
1421				Bounded::max_value(),
1422				MigrationProcess::<Test>::get()
1423			));
1424			// The auto migration halted.
1425			System::assert_last_event(
1426				crate::Event::Halted { error: Error::<Test>::KeyTooLong }.into(),
1427			);
1428			// Limits are killed.
1429			assert!(AutoLimits::<Test>::get().is_none());
1430
1431			// Calling `migrate_until_exhaustion` also fails.
1432			let mut task = StateTrieMigration::migration_process();
1433			let result = task.migrate_until_exhaustion(
1434				StateTrieMigration::signed_migration_max_limits().unwrap(),
1435			);
1436			assert!(result.is_err());
1437		});
1438	}
1439
1440	#[test]
1441	fn detects_value_in_empty_top_key() {
1442		let limit = MigrationLimits { item: 1, size: 1000 };
1443		let initial_keys = Some(vec![(vec![], vec![66u8; 77])]);
1444		let mut ext = new_test_ext(StateVersion::V0, false, initial_keys.clone(), None);
1445
1446		let root_upgraded = ext.execute_with(|| {
1447			AutoLimits::<Test>::put(Some(limit));
1448			let root = run_to_block(30).0;
1449
1450			// eventually everything is over.
1451			assert!(StateTrieMigration::migration_process().finished());
1452			root
1453		});
1454
1455		let mut ext2 = new_test_ext(StateVersion::V1, false, initial_keys, None);
1456		let root = ext2.execute_with(|| {
1457			AutoLimits::<Test>::put(Some(limit));
1458			run_to_block(30).0
1459		});
1460
1461		assert_eq!(root, root_upgraded);
1462	}
1463
1464	#[test]
1465	fn detects_value_in_first_child_key() {
1466		let limit = MigrationLimits { item: 1, size: 1000 };
1467		let initial_child = Some(vec![(b"chk1".to_vec(), vec![], vec![66u8; 77])]);
1468		let mut ext = new_test_ext(StateVersion::V0, false, None, initial_child.clone());
1469
1470		let root_upgraded = ext.execute_with(|| {
1471			AutoLimits::<Test>::put(Some(limit));
1472			let root = run_to_block(30).0;
1473
1474			// eventually everything is over.
1475			assert!(StateTrieMigration::migration_process().finished());
1476			root
1477		});
1478
1479		let mut ext2 = new_test_ext(StateVersion::V1, false, None, initial_child);
1480		let root = ext2.execute_with(|| {
1481			AutoLimits::<Test>::put(Some(limit));
1482			run_to_block(30).0
1483		});
1484
1485		assert_eq!(root, root_upgraded);
1486	}
1487
1488	#[test]
1489	fn auto_migrate_works() {
1490		let run_with_limits = |limit, from, until| {
1491			let mut ext = new_test_ext(StateVersion::V0, false, None, None);
1492			let root_upgraded = ext.execute_with(|| {
1493				assert_eq!(AutoLimits::<Test>::get(), None);
1494				assert_eq!(MigrationProcess::<Test>::get(), Default::default());
1495
1496				// nothing happens if we don't set the limits.
1497				let _ = run_to_block(from);
1498				assert_eq!(MigrationProcess::<Test>::get(), Default::default());
1499
1500				// this should allow 1 item per block to be migrated.
1501				AutoLimits::<Test>::put(Some(limit));
1502
1503				let root = run_to_block(until).0;
1504
1505				// eventually everything is over.
1506				assert!(matches!(
1507					StateTrieMigration::migration_process(),
1508					MigrationTask { progress_top: Progress::Complete, .. }
1509				));
1510				root
1511			});
1512
1513			let mut ext2 = new_test_ext(StateVersion::V1, false, None, None);
1514			let root = ext2.execute_with(|| {
1515				// update ex2 to contain the new items
1516				let _ = run_to_block(from);
1517				AutoLimits::<Test>::put(Some(limit));
1518				run_to_block(until).0
1519			});
1520			assert_eq!(root, root_upgraded);
1521		};
1522
1523		// single item
1524		run_with_limits(MigrationLimits { item: 1, size: 1000 }, 10, 100);
1525		// multi-item
1526		run_with_limits(MigrationLimits { item: 5, size: 1000 }, 10, 100);
1527		// multi-item, based on size. Note that largest value is 100 bytes.
1528		run_with_limits(MigrationLimits { item: 1000, size: 128 }, 10, 100);
1529		// unbounded
1530		run_with_limits(
1531			MigrationLimits { item: Bounded::max_value(), size: Bounded::max_value() },
1532			10,
1533			100,
1534		);
1535	}
1536
1537	#[test]
1538	fn signed_migrate_works() {
1539		new_test_ext(StateVersion::V0, true, None, None).execute_with(|| {
1540			assert_eq!(MigrationProcess::<Test>::get(), Default::default());
1541
1542			// Allow signed migrations.
1543			SignedMigrationMaxLimits::<Test>::put(MigrationLimits { size: 1024, item: 5 });
1544
1545			// can't submit if limit is too high.
1546			frame_support::assert_err!(
1547				StateTrieMigration::continue_migrate(
1548					RuntimeOrigin::signed(1),
1549					MigrationLimits { item: 5, size: sp_runtime::traits::Bounded::max_value() },
1550					Bounded::max_value(),
1551					MigrationProcess::<Test>::get()
1552				),
1553				Error::<Test>::MaxSignedLimits,
1554			);
1555
1556			// can't submit if poor.
1557			frame_support::assert_err!(
1558				StateTrieMigration::continue_migrate(
1559					RuntimeOrigin::signed(2),
1560					MigrationLimits { item: 5, size: 100 },
1561					100,
1562					MigrationProcess::<Test>::get()
1563				),
1564				Error::<Test>::NotEnoughFunds,
1565			);
1566
1567			// can't submit with bad witness.
1568			frame_support::assert_err_ignore_postinfo!(
1569				StateTrieMigration::continue_migrate(
1570					RuntimeOrigin::signed(1),
1571					MigrationLimits { item: 5, size: 100 },
1572					100,
1573					MigrationTask {
1574						progress_top: Progress::LastKey(bounded_vec![1u8]),
1575						..Default::default()
1576					}
1577				),
1578				Error::<Test>::BadWitness,
1579			);
1580
1581			// migrate all keys in a series of submissions
1582			while !MigrationProcess::<Test>::get().finished() {
1583				// first we compute the task to get the accurate consumption.
1584				let mut task = StateTrieMigration::migration_process();
1585				assert_ok!(task.migrate_until_exhaustion(
1586					StateTrieMigration::signed_migration_max_limits().unwrap(),
1587				));
1588
1589				frame_support::assert_ok!(StateTrieMigration::continue_migrate(
1590					RuntimeOrigin::signed(1),
1591					StateTrieMigration::signed_migration_max_limits().unwrap(),
1592					task.dyn_size,
1593					MigrationProcess::<Test>::get()
1594				));
1595
1596				// no funds should remain reserved.
1597				assert_eq!(Balances::reserved_balance(&1), 0);
1598				assert_eq!(Balances::free_balance(&1), 1000);
1599
1600				// and the task should be updated
1601				assert!(matches!(
1602					StateTrieMigration::migration_process(),
1603					MigrationTask { size: x, .. } if x > 0,
1604				));
1605			}
1606		});
1607	}
1608
1609	#[test]
1610	fn continue_migrate_slashing_works() {
1611		new_test_ext(StateVersion::V0, true, None, None).execute_with(|| {
1612			assert_eq!(MigrationProcess::<Test>::get(), Default::default());
1613
1614			// Allow signed migrations.
1615			SignedMigrationMaxLimits::<Test>::put(MigrationLimits { size: 1024, item: 5 });
1616
1617			// first we compute the task to get the accurate consumption.
1618			let mut task = StateTrieMigration::migration_process();
1619			assert_ok!(task.migrate_until_exhaustion(
1620				StateTrieMigration::signed_migration_max_limits().unwrap(),
1621			));
1622
1623			// can't submit with `real_size_upper` < `task.dyn_size` expect slashing
1624			frame_support::assert_ok!(StateTrieMigration::continue_migrate(
1625				RuntimeOrigin::signed(1),
1626				StateTrieMigration::signed_migration_max_limits().unwrap(),
1627				task.dyn_size - 1,
1628				MigrationProcess::<Test>::get()
1629			));
1630			// no funds should remain reserved.
1631			assert_eq!(Balances::reserved_balance(&1), 0);
1632			// user was slashed
1633			assert_eq!(
1634				Balances::free_balance(&1),
1635				1000 - StateTrieMigration::calculate_deposit_for(5)
1636			);
1637		});
1638	}
1639
1640	#[test]
1641	fn custom_migrate_top_works() {
1642		let correct_witness = 3 + sp_core::storage::TRIE_VALUE_NODE_THRESHOLD * 3 + 1 + 2 + 3;
1643		new_test_ext(StateVersion::V0, true, None, None).execute_with(|| {
1644			frame_support::assert_ok!(StateTrieMigration::migrate_custom_top(
1645				RuntimeOrigin::signed(1),
1646				vec![b"key1".to_vec(), b"key2".to_vec(), b"key3".to_vec()],
1647				correct_witness,
1648			));
1649
1650			// no funds should remain reserved.
1651			assert_eq!(Balances::reserved_balance(&1), 0);
1652			assert_eq!(Balances::free_balance(&1), 1000);
1653		});
1654
1655		new_test_ext(StateVersion::V0, true, None, None).execute_with(|| {
1656			// works if the witness is an overestimate
1657			frame_support::assert_ok!(StateTrieMigration::migrate_custom_top(
1658				RuntimeOrigin::signed(1),
1659				vec![b"key1".to_vec(), b"key2".to_vec(), b"key3".to_vec()],
1660				correct_witness + 99,
1661			));
1662
1663			// no funds should remain reserved.
1664			assert_eq!(Balances::reserved_balance(&1), 0);
1665			assert_eq!(Balances::free_balance(&1), 1000);
1666		});
1667
1668		new_test_ext(StateVersion::V0, true, None, None).execute_with(|| {
1669			assert_eq!(Balances::free_balance(&1), 1000);
1670
1671			// note that we don't expect this to be a noop -- we do slash.
1672			frame_support::assert_ok!(StateTrieMigration::migrate_custom_top(
1673				RuntimeOrigin::signed(1),
1674				vec![b"key1".to_vec(), b"key2".to_vec(), b"key3".to_vec()],
1675				correct_witness - 1,
1676			),);
1677
1678			// no funds should remain reserved.
1679			assert_eq!(Balances::reserved_balance(&1), 0);
1680			assert_eq!(
1681				Balances::free_balance(&1),
1682				1000 - StateTrieMigration::calculate_deposit_for(3)
1683			);
1684		});
1685	}
1686
1687	#[test]
1688	fn custom_migrate_child_works() {
1689		new_test_ext(StateVersion::V0, true, None, None).execute_with(|| {
1690			frame_support::assert_ok!(StateTrieMigration::migrate_custom_child(
1691				RuntimeOrigin::signed(1),
1692				StateTrieMigration::childify("chk1"),
1693				vec![b"key1".to_vec(), b"key2".to_vec()],
1694				55 + 66,
1695			));
1696
1697			// no funds should remain reserved.
1698			assert_eq!(Balances::reserved_balance(&1), 0);
1699			assert_eq!(Balances::free_balance(&1), 1000);
1700		});
1701
1702		new_test_ext(StateVersion::V0, true, None, None).execute_with(|| {
1703			assert_eq!(Balances::free_balance(&1), 1000);
1704
1705			// note that we don't expect this to be a noop -- we do slash.
1706			frame_support::assert_ok!(StateTrieMigration::migrate_custom_child(
1707				RuntimeOrigin::signed(1),
1708				StateTrieMigration::childify("chk1"),
1709				vec![b"key1".to_vec(), b"key2".to_vec()],
1710				999999, // wrong witness
1711			));
1712
1713			// no funds should remain reserved.
1714			assert_eq!(Balances::reserved_balance(&1), 0);
1715			assert_eq!(
1716				Balances::free_balance(&1),
1717				1000 - StateTrieMigration::calculate_deposit_for(2)
1718			);
1719		});
1720	}
1721}
1722
1723/// Exported set of tests to be called against different runtimes.
1724#[cfg(feature = "remote-test")]
1725pub(crate) mod remote_tests {
1726	use crate::{AutoLimits, MigrationLimits, Pallet as StateTrieMigration, LOG_TARGET};
1727	use codec::Encode;
1728	use frame_support::{
1729		traits::{Get, Hooks},
1730		weights::Weight,
1731	};
1732	use frame_system::{pallet_prelude::BlockNumberFor, Pallet as System};
1733	use remote_externalities::Mode;
1734	use sp_core::H256;
1735	use sp_runtime::{
1736		traits::{Block as BlockT, HashingFor, Header as _, One, Zero},
1737		DeserializeOwned,
1738	};
1739	use thousands::Separable;
1740
1741	#[allow(dead_code)]
1742	fn run_to_block<Runtime: crate::Config<Hash = H256>>(
1743		n: BlockNumberFor<Runtime>,
1744	) -> (H256, Weight) {
1745		let mut root = Default::default();
1746		let mut weight_sum = Weight::zero();
1747		while System::<Runtime>::block_number() < n {
1748			System::<Runtime>::set_block_number(System::<Runtime>::block_number() + One::one());
1749			System::<Runtime>::on_initialize(System::<Runtime>::block_number());
1750
1751			weight_sum +=
1752				StateTrieMigration::<Runtime>::on_initialize(System::<Runtime>::block_number());
1753
1754			root = *System::<Runtime>::finalize().state_root();
1755			System::<Runtime>::on_finalize(System::<Runtime>::block_number());
1756		}
1757		(root, weight_sum)
1758	}
1759
1760	/// Run the entire migration, against the given `Runtime`, until completion.
1761	///
1762	/// This will print some very useful statistics, make sure [`crate::LOG_TARGET`] is enabled.
1763	#[allow(dead_code)]
1764	pub(crate) async fn run_with_limits<Runtime, Block>(
1765		limits: MigrationLimits,
1766		mode: Mode<Block::Hash>,
1767	) where
1768		Runtime: crate::Config<Hash = H256>,
1769		Block: BlockT<Hash = H256> + DeserializeOwned,
1770		Block::Header: serde::de::DeserializeOwned,
1771	{
1772		let mut ext = remote_externalities::Builder::<Block>::new()
1773			.mode(mode)
1774			.overwrite_state_version(sp_core::storage::StateVersion::V0)
1775			.build()
1776			.await
1777			.unwrap();
1778
1779		let mut now = ext.execute_with(|| {
1780			AutoLimits::<Runtime>::put(Some(limits));
1781			// requires the block number type in our tests to be same as with mainnet, u32.
1782			frame_system::Pallet::<Runtime>::block_number()
1783		});
1784
1785		let mut duration: BlockNumberFor<Runtime> = Zero::zero();
1786		// set the version to 1, as if the upgrade happened.
1787		ext.state_version = sp_core::storage::StateVersion::V1;
1788
1789		let status =
1790			substrate_state_trie_migration_rpc::migration_status(&ext.as_backend()).unwrap();
1791		assert!(
1792			status.top_remaining_to_migrate > 0,
1793			"no node needs migrating, this probably means that state was initialized with `StateVersion::V1`",
1794		);
1795
1796		log::info!(
1797			target: LOG_TARGET,
1798			"initial check: top_left: {}, child_left: {}, total_top {}, total_child {}",
1799			status.top_remaining_to_migrate.separate_with_commas(),
1800			status.child_remaining_to_migrate.separate_with_commas(),
1801			status.total_top.separate_with_commas(),
1802			status.total_child.separate_with_commas(),
1803		);
1804
1805		loop {
1806			let last_state_root = *ext.backend.root();
1807			let ((finished, weight), proof) = ext.execute_and_prove(|| {
1808				let weight = run_to_block::<Runtime>(now + One::one()).1;
1809				if StateTrieMigration::<Runtime>::migration_process().finished() {
1810					return (true, weight);
1811				}
1812				duration += One::one();
1813				now += One::one();
1814				(false, weight)
1815			});
1816
1817			let compact_proof =
1818				proof.clone().into_compact_proof::<HashingFor<Block>>(last_state_root).unwrap();
1819			log::info!(
1820				target: LOG_TARGET,
1821				"proceeded to #{}, weight: [{} / {}], proof: [{} / {} / {}]",
1822				now,
1823				weight.separate_with_commas(),
1824				<Runtime as frame_system::Config>::BlockWeights::get()
1825					.max_block
1826					.separate_with_commas(),
1827				proof.encoded_size().separate_with_commas(),
1828				compact_proof.encoded_size().separate_with_commas(),
1829				zstd::stream::encode_all(&compact_proof.encode()[..], 0)
1830					.unwrap()
1831					.len()
1832					.separate_with_commas(),
1833			);
1834			ext.commit_all().unwrap();
1835
1836			if finished {
1837				break;
1838			}
1839		}
1840
1841		ext.execute_with(|| {
1842			log::info!(
1843				target: LOG_TARGET,
1844				"finished on_initialize migration in {} block, final state of the task: {:?}",
1845				duration,
1846				StateTrieMigration::<Runtime>::migration_process(),
1847			)
1848		});
1849
1850		let status =
1851			substrate_state_trie_migration_rpc::migration_status(&ext.as_backend()).unwrap();
1852		assert_eq!(status.top_remaining_to_migrate, 0);
1853		assert_eq!(status.child_remaining_to_migrate, 0);
1854	}
1855}
1856
1857#[cfg(all(test, feature = "remote-test"))]
1858mod remote_tests_local {
1859	use super::{
1860		mock::{RuntimeCall as MockCall, *},
1861		remote_tests::run_with_limits,
1862		*,
1863	};
1864	use remote_externalities::{Mode, OfflineConfig, OnlineConfig, SnapshotConfig};
1865	use sp_runtime::traits::Bounded;
1866	use std::env::var as env_var;
1867
1868	// we only use the hash type from this, so using the mock should be fine.
1869	type Extrinsic = sp_runtime::testing::TestXt<MockCall, ()>;
1870	type Block = sp_runtime::testing::Block<Extrinsic>;
1871
1872	#[tokio::test]
1873	async fn on_initialize_migration() {
1874		let snap: SnapshotConfig = env_var("SNAP").expect("Need SNAP env var").into();
1875		let ws_api = env_var("WS_API").expect("Need WS_API env var").into();
1876
1877		sp_tracing::try_init_simple();
1878		let mode = Mode::OfflineOrElseOnline(
1879			OfflineConfig { state_snapshot: snap.clone() },
1880			OnlineConfig { transport: ws_api, state_snapshot: Some(snap), ..Default::default() },
1881		);
1882
1883		// item being the bottleneck
1884		run_with_limits::<Test, Block>(
1885			MigrationLimits { item: 8 * 1024, size: 128 * 1024 * 1024 },
1886			mode.clone(),
1887		)
1888		.await;
1889		// size being the bottleneck
1890		run_with_limits::<Test, Block>(
1891			MigrationLimits { item: Bounded::max_value(), size: 64 * 1024 },
1892			mode,
1893		)
1894		.await;
1895	}
1896}