1use super::*;
20use core::marker::PhantomData;
21use frame_support::{
22 migration::clear_storage_prefix,
23 migrations::{MigrationId, SteppedMigration, SteppedMigrationError},
24 traits::{GetStorageVersion, OnRuntimeUpgrade, UncheckedOnRuntimeUpgrade},
25 weights::WeightMeter,
26};
27
28#[cfg(feature = "try-runtime")]
29use sp_std::vec::Vec;
30
31#[cfg(feature = "try-runtime")]
32use sp_runtime::TryRuntimeError;
33
34pub mod versioned_migrations {
36 use super::*;
37
38 pub type V9ToV10<T, MaxPercentages> = frame_support::migrations::VersionedMigration<
41 9,
42 10,
43 v10::VersionMigrateV9ToV10<T, MaxPercentages>,
44 Pallet<T>,
45 <T as frame_system::Config>::DbWeight,
46 >;
47}
48
49mod v10 {
50 use super::*;
51 use crate::migration::v9::{
52 TierParameters as TierParametersV9, TierThreshold as TierThresholdV9,
53 };
54
55 pub struct VersionMigrateV9ToV10<T, MaxPercentages>(PhantomData<(T, MaxPercentages)>);
56
57 impl<T: Config, MaxPercentages: Get<[Option<Perbill>; 4]>> UncheckedOnRuntimeUpgrade
58 for VersionMigrateV9ToV10<T, MaxPercentages>
59 {
60 fn on_runtime_upgrade() -> Weight {
61 let max_percentages = MaxPercentages::get();
62
63 let result = StaticTierParams::<T>::translate::<TierParametersV9<T::NumberOfTiers>, _>(
65 |maybe_old_params| match maybe_old_params {
66 Some(old_params) => {
67 let new_tier_thresholds: Vec<TierThreshold> = old_params
68 .tier_thresholds
69 .iter()
70 .enumerate()
71 .map(|(idx, old_threshold)| {
72 let maximum_percentage = if idx < max_percentages.len() {
73 max_percentages[idx]
74 } else {
75 None
76 };
77 map_threshold(old_threshold, maximum_percentage)
78 })
79 .collect();
80
81 let tier_thresholds =
82 BoundedVec::<TierThreshold, T::NumberOfTiers>::try_from(
83 new_tier_thresholds,
84 );
85
86 match tier_thresholds {
87 Ok(tier_thresholds) => Some(TierParameters {
88 slot_distribution: old_params.slot_distribution,
89 reward_portion: old_params.reward_portion,
90 tier_thresholds,
91 slot_number_args: old_params.slot_number_args,
92 }),
93 Err(err) => {
94 log::error!(
95 "Failed to convert TierThresholds parameters: {:?}",
96 err
97 );
98 None
99 }
100 }
101 }
102 _ => None,
103 },
104 );
105
106 if result.is_err() {
107 log::error!("Failed to translate StaticTierParams from previous V9 type to current V10 type. Check TierParametersV9 decoding.");
108 ActiveProtocolState::<T>::mutate(|state| {
110 state.maintenance = true;
111 });
112 log::warn!("Maintenance mode enabled.");
113 return T::DbWeight::get().reads_writes(1, 0);
114 }
115
116 T::DbWeight::get().reads_writes(1, 1)
117 }
118
119 #[cfg(feature = "try-runtime")]
120 fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
121 let old_params = v9::StaticTierParams::<T>::get().ok_or_else(|| {
122 TryRuntimeError::Other(
123 "dapp-staking-v3::migration::v10: No old params found for StaticTierParams",
124 )
125 })?;
126 Ok(old_params.encode())
127 }
128
129 #[cfg(feature = "try-runtime")]
130 fn post_upgrade(data: Vec<u8>) -> Result<(), TryRuntimeError> {
131 let old_params: TierParametersV9<T::NumberOfTiers> = Decode::decode(&mut &data[..])
133 .map_err(|_| {
134 TryRuntimeError::Other(
135 "dapp-staking-v3::migration::v10: Failed to decode old values",
136 )
137 })?;
138
139 let new_config = TierConfig::<T>::get();
141 let new_params = StaticTierParams::<T>::get();
142
143 assert!(new_params.is_valid());
145 assert!(new_config.is_valid());
146
147 assert_eq!(
149 old_params.slot_distribution, new_params.slot_distribution,
150 "dapp-staking-v3::migration::v10: Slot distribution has changed"
151 );
152 assert_eq!(
153 old_params.reward_portion, new_params.reward_portion,
154 "dapp-staking-v3::migration::v10: Reward portion has changed"
155 );
156 assert_eq!(
157 old_params.tier_thresholds.len(),
158 new_params.tier_thresholds.len(),
159 "dapp-staking-v3::migration::v10: Number of tier thresholds has changed"
160 );
161
162 for (_, (old_threshold, new_threshold)) in old_params
163 .tier_thresholds
164 .iter()
165 .zip(new_params.tier_thresholds.iter())
166 .enumerate()
167 {
168 match (old_threshold, new_threshold) {
169 (
170 TierThresholdV9::FixedPercentage {
171 required_percentage: old_req,
172 },
173 TierThreshold::FixedPercentage {
174 required_percentage: new_req,
175 },
176 ) => {
177 assert_eq!(
178 old_req, new_req,
179 "dapp-staking-v3::migration::v10: Fixed percentage changed",
180 );
181 }
182 (
183 TierThresholdV9::DynamicPercentage {
184 percentage: old_percentage,
185 minimum_required_percentage: old_min,
186 },
187 TierThreshold::DynamicPercentage {
188 percentage: new_percentage,
189 minimum_required_percentage: new_min,
190 maximum_possible_percentage: _, },
192 ) => {
193 assert_eq!(
194 old_percentage, new_percentage,
195 "dapp-staking-v3::migration::v10: Percentage changed"
196 );
197 assert_eq!(
198 old_min, new_min,
199 "dapp-staking-v3::migration::v10: Minimum percentage changed"
200 );
201 }
202 _ => {
203 return Err(TryRuntimeError::Other(
204 "dapp-staking-v3::migration::v10: Tier threshold type mismatch",
205 ));
206 }
207 }
208 }
209
210 let expected_max_percentages = MaxPercentages::get();
211 for (idx, tier_threshold) in new_params.tier_thresholds.iter().enumerate() {
212 if let TierThreshold::DynamicPercentage {
213 maximum_possible_percentage,
214 ..
215 } = tier_threshold
216 {
217 let expected_maximum_percentage = if idx < expected_max_percentages.len() {
218 expected_max_percentages[idx]
219 } else {
220 None
221 }
222 .unwrap_or(Perbill::from_percent(100));
223 assert_eq!(
224 *maximum_possible_percentage, expected_maximum_percentage,
225 "dapp-staking-v3::migration::v10: Max percentage differs from expected",
226 );
227 }
228 }
229
230 ensure!(
232 Pallet::<T>::on_chain_storage_version() >= 10,
233 "dapp-staking-v3::migration::v10: Wrong storage version."
234 );
235
236 Ok(())
237 }
238 }
239
240 pub fn map_threshold(old: &TierThresholdV9, max_percentage: Option<Perbill>) -> TierThreshold {
241 match old {
242 TierThresholdV9::FixedPercentage {
243 required_percentage,
244 } => TierThreshold::FixedPercentage {
245 required_percentage: *required_percentage,
246 },
247 TierThresholdV9::DynamicPercentage {
248 percentage,
249 minimum_required_percentage,
250 } => TierThreshold::DynamicPercentage {
251 percentage: *percentage,
252 minimum_required_percentage: *minimum_required_percentage,
253 maximum_possible_percentage: max_percentage.unwrap_or(Perbill::from_percent(100)), },
255 }
256 }
257}
258
259mod v9 {
260 use super::*;
261 use frame_support::storage_alias;
262
263 #[derive(Encode, Decode)]
264 pub struct TierParameters<NT: Get<u32>> {
265 pub reward_portion: BoundedVec<Permill, NT>,
266 pub slot_distribution: BoundedVec<Permill, NT>,
267 pub tier_thresholds: BoundedVec<TierThreshold, NT>,
268 pub slot_number_args: (u64, u64),
269 }
270
271 #[derive(Encode, Decode)]
272 pub enum TierThreshold {
273 FixedPercentage {
274 required_percentage: Perbill,
275 },
276 DynamicPercentage {
277 percentage: Perbill,
278 minimum_required_percentage: Perbill,
279 },
280 }
281
282 #[storage_alias]
284 pub type StaticTierParams<T: Config> =
285 StorageValue<Pallet<T>, TierParameters<<T as Config>::NumberOfTiers>>;
286}
287
288const PALLET_MIGRATIONS_ID: &[u8; 16] = b"dapp-staking-mbm";
289
290pub struct LazyMigration<T, W: WeightInfo>(PhantomData<(T, W)>);
291
292impl<T: Config, W: WeightInfo> SteppedMigration for LazyMigration<T, W> {
293 type Cursor = <T as frame_system::Config>::AccountId;
294 type Identifier = MigrationId<16>;
296
297 fn id() -> Self::Identifier {
299 MigrationId {
300 pallet_id: *PALLET_MIGRATIONS_ID,
301 version_from: 0,
302 version_to: 1,
303 }
304 }
305
306 fn step(
307 mut cursor: Option<Self::Cursor>,
308 meter: &mut WeightMeter,
309 ) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
310 let required = W::step();
311 if meter.remaining().any_lt(required) {
315 return Err(SteppedMigrationError::InsufficientWeight { required });
316 }
317
318 let mut count = 0u32;
319 let mut migrated = 0u32;
320 let current_block_number =
321 frame_system::Pallet::<T>::block_number().saturated_into::<u32>();
322
323 loop {
325 if meter.try_consume(required).is_err() {
326 break;
327 }
328
329 let mut iter = if let Some(last_key) = cursor {
330 Ledger::<T>::iter_from(Ledger::<T>::hashed_key_for(last_key))
335 } else {
336 Ledger::<T>::iter()
338 };
339
340 if let Some((ref last_key, mut ledger)) = iter.next() {
342 count.saturating_inc();
344
345 if ledger.unlocking.is_empty() {
346 cursor = Some(last_key.clone());
349 continue;
350 }
351 for chunk in ledger.unlocking.iter_mut() {
352 if current_block_number >= chunk.unlock_block {
353 continue; }
355 let remaining_blocks = chunk.unlock_block.saturating_sub(current_block_number);
356 chunk.unlock_block.saturating_accrue(remaining_blocks);
357 }
358
359 Ledger::<T>::insert(last_key, ledger);
361
362 migrated.saturating_inc();
364
365 cursor = Some(last_key.clone())
367 } else {
368 cursor = None;
370 break;
371 }
372 }
373 log::info!(target: LOG_TARGET, "🚚 iterated {count} entries, migrated {migrated}");
374 Ok(cursor)
375 }
376}
377
378pub struct AdjustEraMigration<T>(PhantomData<T>);
380
381impl<T: Config> OnRuntimeUpgrade for AdjustEraMigration<T> {
382 fn on_runtime_upgrade() -> Weight {
383 log::info!("🚚 migrated to async backing, adjust next era start");
384 ActiveProtocolState::<T>::mutate_exists(|maybe| {
385 if let Some(state) = maybe {
386 let current_block_number =
387 frame_system::Pallet::<T>::block_number().saturated_into::<u32>();
388 let remaining = state.next_era_start.saturating_sub(current_block_number);
389 state.next_era_start.saturating_accrue(remaining);
390 }
391 });
392 T::DbWeight::get().reads_writes(1, 1)
393 }
394}
395
396pub const EXPECTED_PALLET_DAPP_STAKING_VERSION: u16 = 9;
397
398pub struct DappStakingCleanupMigration<T>(PhantomData<T>);
399impl<T: Config> OnRuntimeUpgrade for DappStakingCleanupMigration<T> {
400 fn on_runtime_upgrade() -> Weight {
401 let dapp_staking_storage_version =
402 <Pallet<T> as GetStorageVersion>::on_chain_storage_version();
403 if dapp_staking_storage_version != EXPECTED_PALLET_DAPP_STAKING_VERSION {
404 log::info!("Aborting migration due to unexpected on-chain storage versions for pallet-dapp-staking: {:?}. Expectation was: {:?}.", dapp_staking_storage_version, EXPECTED_PALLET_DAPP_STAKING_VERSION);
405 return T::DbWeight::get().reads(1);
406 }
407
408 let pallet_prefix: &[u8] = b"DappStaking";
409 let result =
410 clear_storage_prefix(pallet_prefix, b"ActiveBonusUpdateState", &[], None, None);
411 log::info!(
412 "cleanup dAppStaking migration result: {:?}",
413 result.deconstruct()
414 );
415
416 T::DbWeight::get().reads_writes(1, 1)
417 }
418}