1use super::*;
20use astar_primitives::dapp_staking::FIXED_TIER_SLOTS_ARGS;
21use core::marker::PhantomData;
22use frame_support::traits::UncheckedOnRuntimeUpgrade;
23
24#[cfg(feature = "try-runtime")]
25mod try_runtime_imports {
26 pub use sp_runtime::TryRuntimeError;
27}
28
29#[cfg(feature = "try-runtime")]
30use try_runtime_imports::*;
31
32pub mod versioned_migrations {
34 use super::*;
35
36 pub type V10ToV11<T, TierParamsConfig, OldErasVoting, OldErasBnE> =
39 frame_support::migrations::VersionedMigration<
40 10,
41 11,
42 v11::VersionMigrateV10ToV11<T, TierParamsConfig, OldErasVoting, OldErasBnE>,
43 Pallet<T>,
44 <T as frame_system::Config>::DbWeight,
45 >;
46}
47
48pub trait TierParamsV11Config {
50 fn reward_portion() -> [Permill; 4];
51 fn slot_distribution() -> [Permill; 4];
52 fn tier_thresholds() -> [TierThreshold; 4];
53 fn slot_number_args() -> (u64, u64);
54 fn tier_rank_multipliers() -> [u32; 4];
55}
56
57pub struct DefaultTierParamsV11;
58impl TierParamsV11Config for DefaultTierParamsV11 {
59 fn reward_portion() -> [Permill; 4] {
60 [
61 Permill::from_percent(0),
62 Permill::from_percent(70),
63 Permill::from_percent(30),
64 Permill::from_percent(0),
65 ]
66 }
67
68 fn slot_distribution() -> [Permill; 4] {
69 [
70 Permill::from_percent(0),
71 Permill::from_parts(375_000), Permill::from_parts(625_000), Permill::from_percent(0),
74 ]
75 }
76
77 fn tier_thresholds() -> [TierThreshold; 4] {
78 [
79 TierThreshold::FixedPercentage {
80 required_percentage: Perbill::from_parts(23_200_000), },
82 TierThreshold::FixedPercentage {
83 required_percentage: Perbill::from_parts(9_300_000), },
85 TierThreshold::FixedPercentage {
86 required_percentage: Perbill::from_parts(3_500_000), },
88 TierThreshold::FixedPercentage {
90 required_percentage: Perbill::from_parts(0), },
92 ]
93 }
94
95 fn slot_number_args() -> (u64, u64) {
96 FIXED_TIER_SLOTS_ARGS
97 }
98
99 fn tier_rank_multipliers() -> [u32; 4] {
100 [0, 24_000, 46_700, 0]
101 }
102}
103
104mod v11 {
105 use super::*;
106
107 pub struct VersionMigrateV10ToV11<T, P, OldErasVoting, OldErasBnE>(
108 PhantomData<(T, P, OldErasVoting, OldErasBnE)>,
109 );
110
111 impl<T: Config, P: TierParamsV11Config, OldErasVoting: Get<u32>, OldErasBnE: Get<u32>>
112 UncheckedOnRuntimeUpgrade for VersionMigrateV10ToV11<T, P, OldErasVoting, OldErasBnE>
113 {
114 fn on_runtime_upgrade() -> Weight {
115 let old_eras_voting = OldErasVoting::get();
116 let old_eras_bne = OldErasBnE::get();
117
118 let mut reads: u64 = 0;
119 let mut writes: u64 = 0;
120
121 let current_integrated_dapps = IntegratedDApps::<T>::count();
123 reads += 1;
124
125 let max_dapps_allowed = T::MaxNumberOfContracts::get();
126
127 if current_integrated_dapps > max_dapps_allowed {
128 log::warn!(
129 target: LOG_TARGET,
130 "Safety net triggered: {} dApps exceed limit of {}. Removing {} excess dApps.",
131 current_integrated_dapps,
132 max_dapps_allowed,
133 current_integrated_dapps - max_dapps_allowed
134 );
135
136 let excess = current_integrated_dapps.saturating_sub(max_dapps_allowed);
137 let victims: Vec<_> = IntegratedDApps::<T>::iter()
138 .take(excess as usize)
139 .map(|(contract, dapp_info)| (contract, dapp_info.id))
140 .collect();
141
142 reads += excess as u64;
143
144 for (contract, dapp_id) in victims {
145 ContractStake::<T>::remove(&dapp_id);
146 IntegratedDApps::<T>::remove(&contract);
147 writes += 2;
148
149 let current_era = ActiveProtocolState::<T>::get().era;
150 Pallet::<T>::deposit_event(Event::<T>::DAppUnregistered {
151 smart_contract: contract,
152 era: current_era,
153 });
154 log::info!(
155 target: LOG_TARGET,
156 "Safety net removed dApp ID {} (contract: {:?})",
157 dapp_id,
158 core::any::type_name::<T::SmartContract>()
159 );
160 }
161
162 reads += 1;
164 }
165
166 let reward_portion = BoundedVec::<Permill, T::NumberOfTiers>::truncate_from(
168 P::reward_portion().to_vec(),
169 );
170 let slot_distribution = BoundedVec::<Permill, T::NumberOfTiers>::truncate_from(
171 P::slot_distribution().to_vec(),
172 );
173 let tier_thresholds = BoundedVec::<TierThreshold, T::NumberOfTiers>::truncate_from(
174 P::tier_thresholds().to_vec(),
175 );
176 let tier_rank_multipliers = BoundedVec::<u32, T::NumberOfTiers>::truncate_from(
177 P::tier_rank_multipliers().to_vec(),
178 );
179
180 let new_params = TierParameters::<T::NumberOfTiers> {
181 reward_portion,
182 slot_distribution,
183 tier_thresholds,
184 slot_number_args: P::slot_number_args(),
185 tier_rank_multipliers,
186 };
187
188 if !new_params.is_valid() {
189 log::error!(
190 target: LOG_TARGET,
191 "New TierParameters validation failed. Enabling maintenance mode."
192 );
193
194 ActiveProtocolState::<T>::mutate(|state| {
196 state.maintenance = true;
197 });
198 reads += 1;
199 writes += 1;
200
201 return T::DbWeight::get().reads_writes(reads, writes);
202 }
203
204 StaticTierParams::<T>::put(new_params);
206 writes += 1;
207 log::info!(target: LOG_TARGET, "StaticTierParams updated successfully");
208
209 ActiveProtocolState::<T>::mutate(|state| {
212 if state.period_info.subperiod == Subperiod::Voting {
213 let current_block: u32 =
214 frame_system::Pallet::<T>::block_number().saturated_into();
215
216 let old_voting_length: u32 =
218 old_eras_voting.saturating_mul(T::CycleConfiguration::blocks_per_era());
219 let new_voting_length: u32 = Pallet::<T>::blocks_per_voting_period()
220 .max(T::CycleConfiguration::blocks_per_era());
221
222 let remaining_old: u32 = state.next_era_start.saturating_sub(current_block);
224 let elapsed: u32 = old_voting_length.saturating_sub(remaining_old);
225
226 let remaining_new: u32 = new_voting_length.saturating_sub(elapsed);
228
229 state.next_era_start = if remaining_new == 0 {
232 current_block.saturating_add(1)
233 } else {
234 current_block.saturating_add(remaining_new)
235 };
236
237 log::info!(
238 target: LOG_TARGET,
239 "ActiveProtocolState updated: next_era_start (old_length={}, new_length={}, elapsed={}, remaining_new={})",
240 old_voting_length,
241 new_voting_length,
242 elapsed,
243 remaining_new
244 );
245 }
246 if state.period_info.subperiod == Subperiod::Voting {
247 let current_block: u32 =
249 frame_system::Pallet::<T>::block_number().saturated_into();
250 let new_voting_length: u32 = Pallet::<T>::blocks_per_voting_period()
251 .max(T::CycleConfiguration::blocks_per_era());
252 let remaining_old: u32 = state.next_era_start.saturating_sub(current_block);
253 let remaining_new: u32 = remaining_old.min(new_voting_length).max(1);
256
257 state.next_era_start = current_block.saturating_add(remaining_new);
258
259 log::info!(
260 target: LOG_TARGET,
261 "ActiveProtocolState updated: next_era_start (remaining_old={}, remaining_new={})",
262 remaining_old,
263 remaining_new
264 );
265 } else {
266 let new_eras_total: EraNumber =
268 T::CycleConfiguration::eras_per_build_and_earn_subperiod();
269
270 let current_era: EraNumber = state.era;
272 let old_end: EraNumber = state.period_info.next_subperiod_start_era;
273
274 let remaining_old: EraNumber = old_end.saturating_sub(current_era);
275 let elapsed: EraNumber = old_eras_bne.saturating_sub(remaining_old);
276
277 let remaining_new: EraNumber = new_eras_total.saturating_sub(elapsed);
278
279 state.period_info.next_subperiod_start_era =
280 current_era.saturating_add(remaining_new);
281
282 log::info!(
283 target: LOG_TARGET,
284 "ActiveProtocolState updated: next_subperiod_start_era (remainder-adjusted)"
285 );
286 }
287 });
288 reads += 1;
289 writes += 1;
290
291 T::DbWeight::get().reads_writes(reads, writes)
292 }
293
294 #[cfg(feature = "try-runtime")]
295 fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
296 let protocol_state = ActiveProtocolState::<T>::get();
297 let current_block: u32 = frame_system::Pallet::<T>::block_number().saturated_into();
298
299 let pre_dapp_count = IntegratedDApps::<T>::count();
300 let max_allowed = T::MaxNumberOfContracts::get();
301
302 log::info!(
303 target: LOG_TARGET,
304 "Pre-upgrade: dApp count={}, max={}, cleanup_needed={}",
305 pre_dapp_count,
306 max_allowed,
307 pre_dapp_count > max_allowed
308 );
309
310 Ok((
311 protocol_state.period_info.subperiod,
312 protocol_state.era,
313 protocol_state.next_era_start,
314 protocol_state.period_info.next_subperiod_start_era,
315 current_block,
316 pre_dapp_count,
317 max_allowed,
318 )
319 .encode())
320 }
321
322 #[cfg(feature = "try-runtime")]
323 fn post_upgrade(data: Vec<u8>) -> Result<(), TryRuntimeError> {
324 let (
325 old_subperiod,
326 old_era,
327 old_next_era_start,
328 old_next_subperiod_era,
329 pre_block,
330 pre_dapp_count,
331 max_allowed,
332 ): (Subperiod, EraNumber, u32, EraNumber, u32, u32, u32) =
333 Decode::decode(&mut &data[..])
334 .map_err(|_| TryRuntimeError::Other("Failed to decode pre-upgrade data"))?;
335 let old_eras_voting = OldErasVoting::get();
336 let old_eras_bne = OldErasBnE::get();
337
338 ensure!(
340 Pallet::<T>::on_chain_storage_version() == StorageVersion::new(11),
341 "Storage version should be 11"
342 );
343
344 let post_dapp_count = IntegratedDApps::<T>::count();
346 log::debug!(
347 "post_dapp_count={}, max_allowed={}",
348 post_dapp_count,
349 max_allowed
350 );
351 ensure!(
352 post_dapp_count <= max_allowed,
353 "dApp count still exceeds limit",
354 );
355
356 if pre_dapp_count > max_allowed {
357 let expected_removed = pre_dapp_count - max_allowed;
358 let actual_removed = pre_dapp_count - post_dapp_count;
359 log::debug!(
360 "Removed {} dApps, expected to remove {}",
361 actual_removed,
362 expected_removed
363 );
364 ensure!(
365 actual_removed == expected_removed,
366 "Mismatch in the expected dApps to be unregistered"
367 );
368 }
369
370 let new_params = StaticTierParams::<T>::get();
372 ensure!(new_params.is_valid(), "New tier params invalid");
373 ensure!(
374 new_params.reward_portion.as_slice() == P::reward_portion(),
375 "reward_portion mismatch"
376 );
377 ensure!(
378 new_params.tier_rank_multipliers.as_slice() == P::tier_rank_multipliers(),
379 "tier_rank_multipliers mismatch"
380 );
381
382 let protocol_state = ActiveProtocolState::<T>::get();
384 ensure!(!protocol_state.maintenance, "Maintenance mode enabled");
385
386 if old_subperiod == Subperiod::Voting {
387 let old_voting_length: u32 =
388 old_eras_voting.saturating_mul(T::CycleConfiguration::blocks_per_era());
389 let new_voting_length: u32 = Pallet::<T>::blocks_per_voting_period();
390
391 let remaining_old: u32 = old_next_era_start.saturating_sub(pre_block);
392 let elapsed: u32 = old_voting_length.saturating_sub(remaining_old);
393 let remaining_new: u32 = new_voting_length.saturating_sub(elapsed);
394
395 let expected = if remaining_new == 0 {
396 pre_block.saturating_add(1)
397 } else {
398 pre_block.saturating_add(remaining_new)
399 };
400
401 ensure!(
402 protocol_state.next_era_start == expected,
403 "Voting: next_era_start incorrect"
404 );
405 } else {
406 let new_total: EraNumber =
407 T::CycleConfiguration::eras_per_build_and_earn_subperiod();
408 let remaining_old: EraNumber = old_next_subperiod_era.saturating_sub(old_era);
409 let elapsed: EraNumber = old_eras_bne.saturating_sub(remaining_old);
410 let remaining_new: EraNumber = new_total.saturating_sub(elapsed);
411 let expected: EraNumber = old_era.saturating_add(remaining_new);
412
413 ensure!(
414 protocol_state.period_info.next_subperiod_start_era == expected,
415 "BuildEarn: next_subperiod_start_era incorrect"
416 );
417 ensure!(
418 old_next_era_start > expected,
419 "next_era_start did not update as expected"
420 );
421 }
422
423 log::info!(target: LOG_TARGET, "Post-upgrade: All checks passed");
424 Ok(())
425 }
426 }
427}
428
429mod v10 {
430 use super::*;
431 use frame_support::storage_alias;
432
433 #[derive(Encode, Decode, Clone)]
435 pub struct TierParameters<NT: Get<u32>> {
436 pub reward_portion: BoundedVec<Permill, NT>,
437 pub slot_distribution: BoundedVec<Permill, NT>,
438 pub tier_thresholds: BoundedVec<TierThreshold, NT>,
439 pub slot_number_args: (u64, u64),
440 }
441
442 #[storage_alias]
443 pub type StaticTierParams<T: Config> =
444 StorageValue<Pallet<T>, TierParameters<<T as Config>::NumberOfTiers>, OptionQuery>;
445}