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