pallet_staking_async/
migrations.rs1use crate::{log, reward::EraRewardManager, Config, DisableMintingGuard, RewardKind, RewardPot};
21use frame_support::{
22 pallet_prelude::*,
23 traits::{
24 fungible::{Inspect, Mutate},
25 tokens::Preservation,
26 Get, OnRuntimeUpgrade,
27 },
28 PalletId,
29};
30use sp_runtime::{traits::AccountIdConversion, Saturating};
31use sp_staking::EraIndex;
32
33pub struct MigrateEraPotsToPool<T, S, K>(core::marker::PhantomData<(T, S, K)>);
47
48impl<T: Config, S: Get<PalletId>, K: Get<RewardKind>> MigrateEraPotsToPool<T, S, K> {
49 fn old_pot_account(era: EraIndex) -> T::AccountId {
52 S::get().into_sub_account_truncating(RewardPot::Era(era, K::get()))
53 }
54}
55
56impl<T: Config, S: Get<PalletId>, K: Get<RewardKind>> OnRuntimeUpgrade
57 for MigrateEraPotsToPool<T, S, K>
58{
59 fn on_runtime_upgrade() -> Weight {
60 let mut weight = T::DbWeight::get().reads(2);
61
62 let Some(guard_era) = DisableMintingGuard::<T>::get() else {
63 log!(info, "EraPotsToPool: guard unset, nothing to migrate");
64 return weight;
65 };
66
67 let active_era_idx = crate::session_rotation::Rotator::<T>::active_era();
68 debug_assert!(
69 active_era_idx >= guard_era,
70 "active_era should always be past DisableMintingGuard once set"
71 );
72 if active_era_idx <= guard_era {
73 return weight;
74 }
75
76 let oldest = active_era_idx.saturating_sub(T::HistoryDepth::get()).max(guard_era);
79
80 let kind = K::get();
81 let mut migrated = 0u32;
82 for era in oldest..active_era_idx {
83 let old = Self::old_pot_account(era);
84 weight.saturating_accrue(T::DbWeight::get().reads(1));
85 if frame_system::Pallet::<T>::providers(&old) == 0 {
86 continue;
87 }
88
89 let new = EraRewardManager::<T>::create(era, kind);
92 weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
93
94 let balance = T::Currency::balance(&old);
95 weight.saturating_accrue(T::DbWeight::get().reads(1));
96 if !balance.is_zero() {
97 if let Err(e) = T::Currency::transfer(&old, &new, balance, Preservation::Expendable)
98 {
99 log!(
100 error,
101 "EraPotsToPool: era {} kind {:?}: transfer failed: {:?}",
102 era,
103 kind,
104 e,
105 );
106 continue;
109 }
110 weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2));
111 }
112
113 let _ = frame_system::Pallet::<T>::dec_providers(&old);
115 weight.saturating_accrue(T::DbWeight::get().writes(1));
116 migrated.saturating_accrue(1);
117 }
118
119 log!(
120 info,
121 "EraPotsToPool: migrated {} eras of kind {:?} from guard {} to active {}",
122 migrated,
123 kind,
124 guard_era,
125 active_era_idx,
126 );
127 weight
128 }
129
130 #[cfg(feature = "try-runtime")]
131 fn pre_upgrade() -> Result<alloc::vec::Vec<u8>, sp_runtime::TryRuntimeError> {
132 use crate::{BalanceOf, PotAccountProvider};
133 use codec::Encode;
134 use sp_runtime::traits::Zero;
135
136 let kind = K::get();
137 let mut total_old: BalanceOf<T> = Zero::zero();
138 let mut total_new_pre: BalanceOf<T> = Zero::zero();
139 for era in Self::migrated_eras() {
140 let old = Self::old_pot_account(era);
141 total_old.saturating_accrue(T::Currency::balance(&old));
142 let new = T::RewardPots::pot_account(RewardPot::Era(era, kind));
143 total_new_pre.saturating_accrue(T::Currency::balance(&new));
144 }
145 Ok((total_old, total_new_pre).encode())
146 }
147
148 #[cfg(feature = "try-runtime")]
149 fn post_upgrade(state: alloc::vec::Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
150 use crate::{BalanceOf, PotAccountProvider};
151 use codec::Decode;
152 use sp_runtime::traits::Zero;
153
154 let (total_old, total_new_pre): (BalanceOf<T>, BalanceOf<T>) =
155 Decode::decode(&mut &state[..]).map_err(|_| "decode pre_upgrade state")?;
156
157 let kind = K::get();
158 let mut remaining_old: BalanceOf<T> = Zero::zero();
159 let mut total_new_post: BalanceOf<T> = Zero::zero();
160 for era in Self::migrated_eras() {
161 let old = Self::old_pot_account(era);
162 remaining_old.saturating_accrue(T::Currency::balance(&old));
163 let new = T::RewardPots::pot_account(RewardPot::Era(era, kind));
164 total_new_post.saturating_accrue(T::Currency::balance(&new));
165 }
166
167 frame_support::ensure!(
168 remaining_old.is_zero(),
169 "old pot accounts still hold balance after migration"
170 );
171 frame_support::ensure!(
174 total_new_post.saturating_sub(total_new_pre) == total_old,
175 "new pot balances did not increase by total_old after migration"
176 );
177 Ok(())
178 }
179}
180
181#[cfg(feature = "try-runtime")]
182impl<T: Config, S: Get<PalletId>, K: Get<RewardKind>> MigrateEraPotsToPool<T, S, K> {
183 fn migrated_eras() -> core::ops::Range<EraIndex> {
185 let active = crate::session_rotation::Rotator::<T>::active_era();
186 match DisableMintingGuard::<T>::get() {
187 Some(guard) if active > guard => {
188 let oldest = active.saturating_sub(T::HistoryDepth::get()).max(guard);
189 oldest..active
190 },
191 _ => 0..0,
192 }
193 }
194}