|
| 1 | +use codec::{Decode, Encode}; |
| 2 | +use frame_support::{ |
| 3 | + log, |
| 4 | + storage::{unhashed, StoragePrefixedMap}, |
| 5 | + traits::{Currency, Get}, |
| 6 | + weights::Weight, |
| 7 | + BoundedVec, |
| 8 | +}; |
| 9 | +use sp_runtime::traits::CheckedDiv; |
| 10 | +pub(crate) type BalanceOf<T> = <<T as orml_vesting::Config>::Currency as Currency< |
| 11 | + <T as frame_system::Config>::AccountId, |
| 12 | +>>::Balance; |
| 13 | +pub(crate) type VestingScheduleOf<T> = |
| 14 | + orml_vesting::VestingSchedule<<T as frame_system::Config>::BlockNumber, BalanceOf<T>>; |
| 15 | + |
| 16 | +const OLD_START: u32 = 231619; |
| 17 | +const OLD_PERIOD_COUNT: u32 = 2628000; |
| 18 | + |
| 19 | +const NEW_START: u32 = 0; |
| 20 | +const NEW_PERIOD: u32 = 1; |
| 21 | +const NEW_PERIOD_COUNT: u32 = 2160000; |
| 22 | +// total_update_schedules include 10731 items, plan to do two times' runtime upgrade |
| 23 | +const MIGRATION_LIMIT: u32 = 5500; |
| 24 | + |
| 25 | +pub fn migrate<T: orml_vesting::Config>() -> Weight { |
| 26 | + translate_values::<T, BoundedVec<VestingScheduleOf<T>, T::MaxVestingSchedules>, _>(|v| { |
| 27 | + let mut new_v = BoundedVec::default(); |
| 28 | + v.iter().for_each(|vesting_schedule| { |
| 29 | + update_schedule::<T>(vesting_schedule) |
| 30 | + .and_then(|new_schedule| new_v.try_push(new_schedule).ok()); |
| 31 | + }); |
| 32 | + if !new_v.is_empty() && !new_v.len().eq(&v.len()) { |
| 33 | + log::warn!(target: "runtime::orml_vesting", "new schedule is not equal to old"); |
| 34 | + return None; |
| 35 | + } |
| 36 | + if !new_v.is_empty() { |
| 37 | + return Some(new_v); |
| 38 | + } |
| 39 | + None |
| 40 | + }); |
| 41 | + <T as frame_system::Config>::BlockWeights::get().max_block |
| 42 | +} |
| 43 | + |
| 44 | +// https://github.com/paritytech/substrate/blob/polkadot-v0.9.12/frame/support/src/storage/mod.rs#L1168-L1188 |
| 45 | +fn translate_values< |
| 46 | + T: orml_vesting::Config, |
| 47 | + Value: Decode + Encode, |
| 48 | + F: FnMut(Value) -> Option<Value>, |
| 49 | +>( |
| 50 | + mut f: F, |
| 51 | +) { |
| 52 | + log::info!(target: "runtime::orml_vesting", "migrate orml_vesting schedule"); |
| 53 | + let mut count_write = 0u32; |
| 54 | + let mut count_read = 0u32; |
| 55 | + let prefix = orml_vesting::VestingSchedules::<T>::final_prefix(); |
| 56 | + let mut previous_key = prefix.clone().to_vec(); |
| 57 | + while let Some(next) = |
| 58 | + sp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix)) |
| 59 | + { |
| 60 | + if count_write.eq(&MIGRATION_LIMIT) { |
| 61 | + // Avoid terminate block production, so migrate the two within two runtime upgrade |
| 62 | + // refer to https://github.com/paritytech/substrate/issues/10407 |
| 63 | + break; |
| 64 | + } |
| 65 | + count_read += 1; |
| 66 | + previous_key = next; |
| 67 | + let maybe_value = unhashed::get::<Value>(&previous_key); |
| 68 | + match maybe_value { |
| 69 | + Some(value) => match f(value) { |
| 70 | + Some(new) => { |
| 71 | + unhashed::put::<Value>(&previous_key, &new); |
| 72 | + count_write += 1; |
| 73 | + } |
| 74 | + None => continue, |
| 75 | + }, |
| 76 | + None => { |
| 77 | + log::error!("old key failed to decode at {:?}", previous_key); |
| 78 | + continue; |
| 79 | + } |
| 80 | + } |
| 81 | + } |
| 82 | + log::info!( |
| 83 | + target: "runtime::orml_vesting", |
| 84 | + "count_read: {}, count_write: {}", |
| 85 | + count_read, count_write |
| 86 | + ); |
| 87 | +} |
| 88 | + |
| 89 | +fn update_schedule<T: orml_vesting::Config>( |
| 90 | + vesting_schedule: &VestingScheduleOf<T>, |
| 91 | +) -> Option<VestingScheduleOf<T>> { |
| 92 | + if !vesting_schedule.start.eq(&OLD_START.into()) |
| 93 | + || !vesting_schedule.period_count.eq(&OLD_PERIOD_COUNT) |
| 94 | + { |
| 95 | + return None; |
| 96 | + } |
| 97 | + |
| 98 | + vesting_schedule |
| 99 | + .total_amount() |
| 100 | + .and_then(|total| total.checked_div(&NEW_PERIOD_COUNT.into())) |
| 101 | + .and_then(|per_period| { |
| 102 | + Some(VestingScheduleOf::<T> { |
| 103 | + start: NEW_START.into(), |
| 104 | + period: NEW_PERIOD.into(), |
| 105 | + period_count: NEW_PERIOD_COUNT, |
| 106 | + per_period, |
| 107 | + }) |
| 108 | + }) |
| 109 | +} |
| 110 | + |
| 111 | +/// Some checks prior to migration. This can be linked to |
| 112 | +/// [`frame_support::traits::OnRuntimeUpgrade::pre_upgrade`] for further testing. |
| 113 | +/// |
| 114 | +/// Panics if anything goes wrong. |
| 115 | +#[cfg(feature = "try-runtime")] |
| 116 | +pub fn pre_migrate<T: frame_system::Config + orml_vesting::Config>() |
| 117 | +where |
| 118 | + u128: From<BalanceOf<T>>, |
| 119 | +{ |
| 120 | + let mut count_total = 0u64; |
| 121 | + let mut count_one = 0u64; |
| 122 | + let mut count_two = 0u64; |
| 123 | + let mut count_more = 0u64; |
| 124 | + let mut count_need_update = 0u64; |
| 125 | + let mut total_amount: BalanceOf<T> = 0u32.into(); |
| 126 | + orml_vesting::VestingSchedules::<T>::iter().for_each(|(_k, v)| { |
| 127 | + count_total += 1; |
| 128 | + let length = v.len(); |
| 129 | + if length == 1 { |
| 130 | + count_one += 1; |
| 131 | + } else if length == 2 { |
| 132 | + count_two += 1; |
| 133 | + } else if length > 2 { |
| 134 | + count_more += 1; |
| 135 | + } |
| 136 | + v.iter().for_each(|s| { |
| 137 | + if s.start.eq(&OLD_START.into()) && s.period_count.eq(&OLD_PERIOD_COUNT) { |
| 138 | + count_need_update += 1; |
| 139 | + } |
| 140 | + total_amount += s.per_period * s.period_count.into(); |
| 141 | + }); |
| 142 | + }); |
| 143 | + |
| 144 | + // total accounts: 10680, one schedule: 10628, two schedule: 52, more schedule: 0, schedule need update: 10731, total_amount: 31450977794836396000 |
| 145 | + log::info!( |
| 146 | + target: "runtime::orml_vesting", |
| 147 | + "{}, total accounts: {}, one schedule: {}, two schedule: {}, more schedule: {}, schedule need update: {}, total_amount: {:?}", |
| 148 | + "pre-migration", count_total, count_one, count_two, count_more, count_need_update,total_amount |
| 149 | + ); |
| 150 | +} |
| 151 | + |
| 152 | +/// Some checks for after migration. This can be linked to |
| 153 | +/// [`frame_support::traits::OnRuntimeUpgrade::post_upgrade`] for further testing. |
| 154 | +/// |
| 155 | +/// Panics if anything goes wrong. |
| 156 | +#[cfg(feature = "try-runtime")] |
| 157 | +pub fn post_migrate<T: frame_system::Config + orml_vesting::Config>() |
| 158 | +where |
| 159 | + u128: From<BalanceOf<T>>, |
| 160 | +{ |
| 161 | + let mut count_total = 0u64; |
| 162 | + let mut count_one = 0u64; |
| 163 | + let mut count_two = 0u64; |
| 164 | + let mut count_more = 0u64; |
| 165 | + let mut count_success_update = 0u64; |
| 166 | + let mut total_amount: BalanceOf<T> = 0u32.into(); |
| 167 | + orml_vesting::VestingSchedules::<T>::iter().for_each(|(_k, v)| { |
| 168 | + count_total += 1; |
| 169 | + let length = v.len(); |
| 170 | + if length == 1 { |
| 171 | + count_one += 1; |
| 172 | + } else if length == 2 { |
| 173 | + count_two += 1; |
| 174 | + } else if length > 2 { |
| 175 | + count_more += 1; |
| 176 | + } |
| 177 | + v.iter().for_each(|s| { |
| 178 | + if s.start.eq(&NEW_START.into()) && s.period_count.eq(&NEW_PERIOD_COUNT) { |
| 179 | + count_success_update += 1; |
| 180 | + } |
| 181 | + total_amount += s.per_period * s.period_count.into(); |
| 182 | + }); |
| 183 | + }); |
| 184 | + |
| 185 | + // total accounts: 10680, one schedule: 10628, two schedule: 52, more schedule: 0, schedule success update: 10731, total_amount: 31450977784297360000 |
| 186 | + log::info!( |
| 187 | + target: "runtime::orml_vesting", |
| 188 | + "{}, total accounts: {}, one schedule: {}, two schedule: {}, more schedule: {}, schedule success update: {}, total_amount: {:?}", |
| 189 | + "post-migration", count_total, count_one, count_two, count_more, count_success_update, total_amount |
| 190 | + ); |
| 191 | +} |
0 commit comments