1use super::*;
21use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
22use frame_support::{pallet_prelude::*, storage_alias, traits::OnRuntimeUpgrade};
23use log;
24
25#[cfg(feature = "try-runtime")]
26use sp_runtime::TryRuntimeError;
27
28type SystemBlockNumberFor<T> = frame_system::pallet_prelude::BlockNumberFor<T>;
29
30pub mod v0 {
32 use super::*;
33 #[cfg(test)]
37 pub(super) use super::{ReferendumStatus, ReferendumStatusOf};
38
39 pub type ReferendumInfoOf<T, I> = ReferendumInfo<
40 TrackIdOf<T, I>,
41 PalletsOriginOf<T>,
42 SystemBlockNumberFor<T>,
43 BoundedCallOf<T, I>,
44 BalanceOf<T, I>,
45 TallyOf<T, I>,
46 <T as frame_system::Config>::AccountId,
47 ScheduleAddressOf<T, I>,
48 >;
49
50 #[derive(
52 Encode, Decode, Clone, PartialEq, Eq, Debug, TypeInfo, MaxEncodedLen, DecodeWithMemTracking,
53 )]
54 pub enum ReferendumInfo<
55 TrackId: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone,
56 RuntimeOrigin: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone,
57 Moment: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone + EncodeLike,
58 Call: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone,
59 Balance: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone,
60 Tally: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone,
61 AccountId: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone,
62 ScheduleAddress: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone,
63 > {
64 Ongoing(
66 ReferendumStatus<
67 TrackId,
68 RuntimeOrigin,
69 Moment,
70 Call,
71 Balance,
72 Tally,
73 AccountId,
74 ScheduleAddress,
75 >,
76 ),
77 Approved(Moment, Deposit<AccountId, Balance>, Option<Deposit<AccountId, Balance>>),
79 Rejected(Moment, Deposit<AccountId, Balance>, Option<Deposit<AccountId, Balance>>),
81 Cancelled(Moment, Deposit<AccountId, Balance>, Option<Deposit<AccountId, Balance>>),
83 TimedOut(Moment, Deposit<AccountId, Balance>, Option<Deposit<AccountId, Balance>>),
85 Killed(Moment),
87 }
88
89 #[storage_alias]
90 pub type ReferendumInfoFor<T: Config<I>, I: 'static> =
91 StorageMap<Pallet<T, I>, Blake2_128Concat, ReferendumIndex, ReferendumInfoOf<T, I>>;
92}
93
94pub mod v1 {
95 use super::*;
96
97 const TARGET: &'static str = "runtime::referenda::migration::v1";
99
100 pub(crate) type ReferendumInfoOf<T, I> = ReferendumInfo<
101 TrackIdOf<T, I>,
102 PalletsOriginOf<T>,
103 SystemBlockNumberFor<T>,
104 BoundedCallOf<T, I>,
105 BalanceOf<T, I>,
106 TallyOf<T, I>,
107 <T as frame_system::Config>::AccountId,
108 ScheduleAddressOf<T, I>,
109 >;
110
111 #[storage_alias]
112 pub type ReferendumInfoFor<T: Config<I>, I: 'static> =
113 StorageMap<Pallet<T, I>, Blake2_128Concat, ReferendumIndex, ReferendumInfoOf<T, I>>;
114
115 pub struct MigrateV0ToV1<T, I = ()>(PhantomData<(T, I)>);
118 impl<T: Config<I>, I: 'static> OnRuntimeUpgrade for MigrateV0ToV1<T, I> {
119 #[cfg(feature = "try-runtime")]
120 fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
121 let referendum_count = v0::ReferendumInfoFor::<T, I>::iter().count();
122 log::info!(
123 target: TARGET,
124 "pre-upgrade state contains '{}' referendums.",
125 referendum_count
126 );
127 Ok((referendum_count as u32).encode())
128 }
129
130 fn on_runtime_upgrade() -> Weight {
131 let in_code_version = Pallet::<T, I>::in_code_storage_version();
132 let on_chain_version = Pallet::<T, I>::on_chain_storage_version();
133 let mut weight = T::DbWeight::get().reads(1);
134 log::info!(
135 target: TARGET,
136 "running migration with in-code storage version {:?} / onchain {:?}.",
137 in_code_version,
138 on_chain_version
139 );
140 if on_chain_version != 0 {
141 log::warn!(target: TARGET, "skipping migration from v0 to v1.");
142 return weight
143 }
144 v0::ReferendumInfoFor::<T, I>::iter().for_each(|(key, value)| {
145 let maybe_new_value = match value {
146 v0::ReferendumInfo::Ongoing(_) | v0::ReferendumInfo::Killed(_) => None,
147 v0::ReferendumInfo::Approved(e, s, d) =>
148 Some(ReferendumInfo::Approved(e, Some(s), d)),
149 v0::ReferendumInfo::Rejected(e, s, d) =>
150 Some(ReferendumInfo::Rejected(e, Some(s), d)),
151 v0::ReferendumInfo::Cancelled(e, s, d) =>
152 Some(ReferendumInfo::Cancelled(e, Some(s), d)),
153 v0::ReferendumInfo::TimedOut(e, s, d) =>
154 Some(ReferendumInfo::TimedOut(e, Some(s), d)),
155 };
156 if let Some(new_value) = maybe_new_value {
157 weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
158 log::info!(target: TARGET, "migrating referendum #{:?}", &key);
159 v1::ReferendumInfoFor::<T, I>::insert(key, new_value);
160 } else {
161 weight.saturating_accrue(T::DbWeight::get().reads(1));
162 }
163 });
164 StorageVersion::new(1).put::<Pallet<T, I>>();
165 weight.saturating_accrue(T::DbWeight::get().writes(1));
166 weight
167 }
168
169 #[cfg(feature = "try-runtime")]
170 fn post_upgrade(state: Vec<u8>) -> Result<(), TryRuntimeError> {
171 let on_chain_version = Pallet::<T, I>::on_chain_storage_version();
172 ensure!(on_chain_version == 1, "must upgrade from version 0 to 1.");
173 let pre_referendum_count: u32 = Decode::decode(&mut &state[..])
174 .expect("failed to decode the state from pre-upgrade.");
175 let post_referendum_count = ReferendumInfoFor::<T, I>::iter().count() as u32;
176 ensure!(post_referendum_count == pre_referendum_count, "must migrate all referendums.");
177 log::info!(target: TARGET, "migrated all referendums.");
178 Ok(())
179 }
180 }
181}
182
183pub mod switch_block_number_provider {
187 use super::*;
188
189 const TARGET: &'static str = "runtime::referenda::migration::change_block_number_provider";
191 pub trait BlockNumberConversion<Old, New> {
193 fn convert_block_number(block_number: Old) -> New;
197 }
198
199 pub struct MigrateBlockNumberProvider<BlockConverter, T, I = ()>(
201 PhantomData<(T, I)>,
202 PhantomData<BlockConverter>,
203 );
204 impl<BlockConverter: BlockNumberConversion<T, I>, T: Config<I>, I: 'static> OnRuntimeUpgrade
205 for MigrateBlockNumberProvider<BlockConverter, T, I>
206 where
207 BlockConverter: BlockNumberConversion<SystemBlockNumberFor<T>, BlockNumberFor<T, I>>,
208 T: Config<I>,
209 {
210 #[cfg(feature = "try-runtime")]
211 fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
212 let referendum_count = v1::ReferendumInfoFor::<T, I>::iter().count();
213 log::info!(
214 target: TARGET,
215 "pre-upgrade state contains '{}' referendums.",
216 referendum_count
217 );
218 Ok((referendum_count as u32).encode())
219 }
220
221 fn on_runtime_upgrade() -> Weight {
222 let mut weight = Weight::zero();
223 weight.saturating_accrue(migrate_block_number_provider::<BlockConverter, T, I>());
224 weight
225 }
226
227 #[cfg(feature = "try-runtime")]
228 fn post_upgrade(state: Vec<u8>) -> Result<(), TryRuntimeError> {
229 let on_chain_version = Pallet::<T, I>::on_chain_storage_version();
230 ensure!(on_chain_version == 1, "must upgrade from version 1 to 2.");
231 let pre_referendum_count: u32 = Decode::decode(&mut &state[..])
232 .expect("failed to decode the state from pre-upgrade.");
233 let post_referendum_count = ReferendumInfoFor::<T, I>::iter().count() as u32;
234 ensure!(post_referendum_count == pre_referendum_count, "must migrate all referendums.");
235 log::info!(target: TARGET, "migrated all referendums.");
236 Ok(())
237 }
238 }
239
240 pub fn migrate_block_number_provider<BlockConverter, T, I: 'static>() -> Weight
241 where
242 BlockConverter: BlockNumberConversion<SystemBlockNumberFor<T>, BlockNumberFor<T, I>>,
243 T: Config<I>,
244 {
245 let in_code_version = Pallet::<T, I>::in_code_storage_version();
246 let on_chain_version = Pallet::<T, I>::on_chain_storage_version();
247 let mut weight = T::DbWeight::get().reads(1);
248 log::info!(
249 target: "runtime::referenda::migration::change_block_number_provider",
250 "running migration with in-code storage version {:?} / onchain {:?}.",
251 in_code_version,
252 on_chain_version
253 );
254 if on_chain_version == 0 {
255 log::error!(target: TARGET, "skipping migration from v0 to switch_block_number_provider.");
256 return weight
257 }
258
259 v1::ReferendumInfoFor::<T, I>::iter().for_each(|(key, value)| {
261 let maybe_new_value = match value {
262 ReferendumInfo::Ongoing(_) | ReferendumInfo::Killed(_) => None,
263 ReferendumInfo::Approved(e, s, d) => {
264 let new_e = BlockConverter::convert_block_number(e);
265 Some(ReferendumInfo::Approved(new_e, s, d))
266 },
267 ReferendumInfo::Rejected(e, s, d) => {
268 let new_e = BlockConverter::convert_block_number(e);
269 Some(ReferendumInfo::Rejected(new_e, s, d))
270 },
271 ReferendumInfo::Cancelled(e, s, d) => {
272 let new_e = BlockConverter::convert_block_number(e);
273 Some(ReferendumInfo::Cancelled(new_e, s, d))
274 },
275 ReferendumInfo::TimedOut(e, s, d) => {
276 let new_e = BlockConverter::convert_block_number(e);
277 Some(ReferendumInfo::TimedOut(new_e, s, d))
278 },
279 };
280 if let Some(new_value) = maybe_new_value {
281 weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
282 log::info!(target: TARGET, "migrating referendum #{:?}", &key);
283 ReferendumInfoFor::<T, I>::insert(key, new_value);
284 } else {
285 weight.saturating_accrue(T::DbWeight::get().reads(1));
286 }
287 });
288
289 weight
290 }
291}
292
293#[cfg(test)]
294pub mod test {
295 use super::*;
296 use crate::{
297 migration::switch_block_number_provider::{
298 migrate_block_number_provider, BlockNumberConversion,
299 },
300 mock::{Test as T, *},
301 };
302 use core::str::FromStr;
303
304 fn create_status_v0() -> v0::ReferendumStatusOf<T, ()> {
306 let origin: OriginCaller = frame_system::RawOrigin::Root.into();
307 let track = <T as Config<()>>::Tracks::track_for(&origin).unwrap();
308 v0::ReferendumStatusOf::<T, ()> {
309 track,
310 in_queue: true,
311 origin,
312 proposal: set_balance_proposal_bounded(1),
313 enactment: DispatchTime::At(1),
314 tally: TallyOf::<T, ()>::new(track),
315 submission_deposit: Deposit { who: 1, amount: 10 },
316 submitted: 1,
317 decision_deposit: None,
318 alarm: None,
319 deciding: None,
320 }
321 }
322 #[test]
323 pub fn referendum_status_v0() {
324 let ongoing_encoded = sp_core::Bytes::from_str("0x00000000012c01082a0000000000000004000100000000000000010000000000000001000000000000000a00000000000000000000000000000000000100").unwrap();
326 let ongoing_dec = v0::ReferendumInfoOf::<T, ()>::decode(&mut &*ongoing_encoded).unwrap();
327 let ongoing = v0::ReferendumInfoOf::<T, ()>::Ongoing(create_status_v0());
328 assert_eq!(ongoing, ongoing_dec);
329 }
330
331 #[test]
332 fn migration_v0_to_v1_works() {
333 ExtBuilder::default().build_and_execute(|| {
334 let status_v0 = create_status_v0();
336 let ongoing_v0 = v0::ReferendumInfoOf::<T, ()>::Ongoing(status_v0.clone());
337 ReferendumCount::<T, ()>::mutate(|x| x.saturating_inc());
338 v0::ReferendumInfoFor::<T, ()>::insert(2, ongoing_v0);
339 let approved_v0 = v0::ReferendumInfoOf::<T, ()>::Approved(
341 123,
342 Deposit { who: 1, amount: 10 },
343 Some(Deposit { who: 2, amount: 20 }),
344 );
345 ReferendumCount::<T, ()>::mutate(|x| x.saturating_inc());
346 v0::ReferendumInfoFor::<T, ()>::insert(5, approved_v0);
347 v1::MigrateV0ToV1::<T, ()>::on_runtime_upgrade();
349 let ongoing_v1 = v1::ReferendumInfoFor::<T, ()>::get(2).unwrap();
351 assert_eq!(ReferendumInfoOf::<T, ()>::Ongoing(status_v0), ongoing_v1);
353 let approved_v1 = v1::ReferendumInfoFor::<T, ()>::get(5).unwrap();
355 assert_eq!(
356 approved_v1,
357 ReferendumInfoOf::<T, ()>::Approved(
358 123,
359 Some(Deposit { who: 1, amount: 10 }),
360 Some(Deposit { who: 2, amount: 20 })
361 )
362 );
363 });
364 }
365
366 #[test]
367 fn migration_v1_to_switch_block_number_provider_works() {
368 ExtBuilder::default().build_and_execute(|| {
369 pub struct MockBlockConverter;
370
371 impl BlockNumberConversion<SystemBlockNumberFor<T>, BlockNumberFor<T, ()>> for MockBlockConverter {
372 fn convert_block_number(block_number: SystemBlockNumberFor<T>) -> BlockNumberFor<T, ()> {
373 block_number as u64 + 10u64
374 }
375 }
376
377 let referendum_ongoing = v1::ReferendumInfoOf::<T, ()>::Ongoing(create_status_v0());
378 let referendum_approved = v1::ReferendumInfoOf::<T, ()>::Approved(
379 50, Some(Deposit { who: 1, amount: 10 }),
381 Some(Deposit { who: 2, amount: 20 }),
382 );
383
384 ReferendumCount::<T, ()>::mutate(|x| x.saturating_inc());
385 v1::ReferendumInfoFor::<T, ()>::insert(1, referendum_ongoing);
386
387 ReferendumCount::<T, ()>::mutate(|x| x.saturating_inc());
388 v1::ReferendumInfoFor::<T, ()>::insert(2, referendum_approved);
389
390 migrate_block_number_provider::<MockBlockConverter, T, ()>();
391
392 let ongoing_v2 = ReferendumInfoFor::<T, ()>::get(1).unwrap();
393 assert_eq!(
394 ongoing_v2,
395 ReferendumInfoOf::<T, ()>::Ongoing(create_status_v0())
396 );
397
398 let approved_v2 = ReferendumInfoFor::<T, ()>::get(2).unwrap();
399 assert_eq!(
400 approved_v2,
401 ReferendumInfoOf::<T, ()>::Approved(
402 50,
403 Some(Deposit { who: 1, amount: 10 }),
404 Some(Deposit { who: 2, amount: 20 })
405 )
406 );
407 });
408 }
409}