1
// Copyright 2019-2025 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! # Common Moonbeam Migrations
18

            
19
use core::marker::PhantomData;
20
use frame_support::migrations::SteppedMigrationError;
21
use frame_support::weights::WeightMeter;
22
use frame_support::{migrations::SteppedMigration, parameter_types};
23
use pallet_migrations::WeightInfo;
24
use parity_scale_codec::Encode;
25
use sp_core::{twox_128, Get};
26
use sp_io::{storage::clear_prefix, KillStorageResult};
27
use sp_runtime::SaturatedConversion;
28

            
29
/// Remove all of a pallet's state and re-initializes it to the current in-code storage version.
30
///
31
/// It uses the multi block migration frame. Hence it is safe to use even on
32
/// pallets that contain a lot of storage.
33
///
34
/// # Parameters
35
///
36
/// - T: The runtime. Used to access the weight definition.
37
/// - P: The pallet name to be removed as defined in construct runtime
38
///
39
/// # Note
40
///
41
/// If your pallet does rely of some state in genesis you need to take care of that
42
/// separately. This migration only sets the storage version after wiping.
43
pub struct RemovePallet<T, P>(PhantomData<(T, P)>);
44

            
45
impl<T, P> RemovePallet<T, P>
46
where
47
	P: Get<&'static str>,
48
{
49
	#[cfg(feature = "try-runtime")]
50
	fn num_keys() -> u64 {
51
		let prefix = twox_128(P::get().as_bytes()).to_vec();
52
		frame_support::storage::KeyPrefixIterator::new(prefix.clone(), prefix, |_| Ok(())).count()
53
			as _
54
	}
55
}
56

            
57
impl<T, P> SteppedMigration for RemovePallet<T, P>
58
where
59
	T: pallet_migrations::Config,
60
	P: Get<&'static str>,
61
{
62
	type Cursor = bool;
63
	type Identifier = [u8; 16];
64

            
65
6
	fn id() -> Self::Identifier {
66
6
		("RemovePallet::", P::get()).using_encoded(twox_128)
67
6
	}
68

            
69
	fn step(
70
		cursor: Option<Self::Cursor>,
71
		meter: &mut WeightMeter,
72
	) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
73
		// we write the storage version in a seperate block
74
		if cursor.unwrap_or(false) {
75
			let required = T::DbWeight::get().writes(1);
76
			meter
77
				.try_consume(required)
78
				.map_err(|_| SteppedMigrationError::InsufficientWeight { required })?;
79
			return Ok(None);
80
		}
81

            
82
		let base_weight = T::WeightInfo::reset_pallet_migration(0);
83
		let weight_per_key = T::WeightInfo::reset_pallet_migration(1).saturating_sub(base_weight);
84
		let key_budget = meter
85
			.remaining()
86
			.saturating_sub(base_weight)
87
			.checked_div_per_component(&weight_per_key)
88
			.unwrap_or_default()
89
			.saturated_into();
90

            
91
		if key_budget == 0 {
92
			return Err(SteppedMigrationError::InsufficientWeight {
93
				required: T::WeightInfo::reset_pallet_migration(1),
94
			});
95
		}
96

            
97
		let (keys_removed, is_done) =
98
			match clear_prefix(&twox_128(P::get().as_bytes()), Some(key_budget)) {
99
				KillStorageResult::AllRemoved(value) => (value, true),
100
				KillStorageResult::SomeRemaining(value) => (value, false),
101
			};
102

            
103
		meter.consume(T::WeightInfo::reset_pallet_migration(keys_removed));
104

            
105
		Ok(Some(is_done))
106
	}
107

            
108
	#[cfg(feature = "try-runtime")]
109
	fn pre_upgrade() -> Result<sp_std::vec::Vec<u8>, sp_runtime::TryRuntimeError> {
110
		let num_keys: u64 = Self::num_keys();
111
		log::info!(
112
			"RemovePallet<{}>: Trying to remove {num_keys} keys.",
113
			P::get()
114
		);
115
		Ok(num_keys.encode())
116
	}
117

            
118
	#[cfg(feature = "try-runtime")]
119
	fn post_upgrade(state: sp_std::vec::Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
120
		use parity_scale_codec::Decode;
121
		let keys_before = u64::decode(&mut state.as_ref()).expect("We encoded as u64 above; qed");
122
		let keys_now = Self::num_keys();
123
		log::info!(
124
			"RemovePallet<{}>: Keys remaining after migration: {keys_now}",
125
			P::get()
126
		);
127

            
128
		if keys_before <= keys_now {
129
			log::error!("RemovePallet<{}>: Did not remove any keys.", P::get());
130
			Err("RemovePallet failed")?;
131
		}
132

            
133
		if keys_now != 1 {
134
			log::error!("RemovePallet<{}>: Should have a single key after", P::get());
135
			Err("RemovePallet failed")?;
136
		}
137

            
138
		Ok(())
139
	}
140
}
141

            
142
/// Unreleased migrations. Add new ones here:
143
pub type UnreleasedSingleBlockMigrations = ();
144

            
145
/// Migrations/checks that do not need to be versioned and can run on every update.
146
pub type PermanentSingleBlockMigrations<Runtime> =
147
	(pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,);
148

            
149
/// All migrations that will run on the next runtime upgrade.
150
pub type SingleBlockMigrations<Runtime> = (
151
	UnreleasedSingleBlockMigrations,
152
	PermanentSingleBlockMigrations<Runtime>,
153
);
154

            
155
parameter_types! {
156
	pub const AssetManagerPalletName: &'static str = "AssetManager";
157
	pub const AssetsPalletName: &'static str = "Assets";
158
}
159

            
160
/// List of common multiblock migrations to be executed by the pallet_multiblock_migrations.
161
/// The migrations listed here are common to every moonbeam runtime.
162
pub type MultiBlockMigrations<Runtime> = (
163
	RemovePallet<Runtime, AssetManagerPalletName>,
164
	RemovePallet<Runtime, AssetsPalletName>,
165
);