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 },
150 v0::ReferendumInfo::Rejected(e, s, d) => {
151 Some(ReferendumInfo::Rejected(e, Some(s), d))
152 },
153 v0::ReferendumInfo::Cancelled(e, s, d) => {
154 Some(ReferendumInfo::Cancelled(e, Some(s), d))
155 },
156 v0::ReferendumInfo::TimedOut(e, s, d) => {
157 Some(ReferendumInfo::TimedOut(e, Some(s), d))
158 },
159 };
160 if let Some(new_value) = maybe_new_value {
161 weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
162 log::info!(target: TARGET, "migrating referendum #{:?}", &key);
163 v1::ReferendumInfoFor::<T, I>::insert(key, new_value);
164 } else {
165 weight.saturating_accrue(T::DbWeight::get().reads(1));
166 }
167 });
168 StorageVersion::new(1).put::<Pallet<T, I>>();
169 weight.saturating_accrue(T::DbWeight::get().writes(1));
170 weight
171 }
172
173 #[cfg(feature = "try-runtime")]
174 fn post_upgrade(state: Vec<u8>) -> Result<(), TryRuntimeError> {
175 let on_chain_version = Pallet::<T, I>::on_chain_storage_version();
176 ensure!(on_chain_version == 1, "must upgrade from version 0 to 1.");
177 let pre_referendum_count: u32 = Decode::decode(&mut &state[..])
178 .expect("failed to decode the state from pre-upgrade.");
179 let post_referendum_count = ReferendumInfoFor::<T, I>::iter().count() as u32;
180 ensure!(post_referendum_count == pre_referendum_count, "must migrate all referendums.");
181 log::info!(target: TARGET, "migrated all referendums.");
182 Ok(())
183 }
184 }
185}
186
187pub mod switch_block_number_provider {
191 use super::*;
192
193 const TARGET: &'static str = "runtime::referenda::migration::change_block_number_provider";
195 pub trait BlockNumberConversion<Old, New> {
197 fn convert_block_number(block_number: Old) -> New;
201 }
202
203 pub struct MigrateBlockNumberProvider<BlockConverter, T, I = ()>(
205 PhantomData<(T, I)>,
206 PhantomData<BlockConverter>,
207 );
208 impl<BlockConverter: BlockNumberConversion<T, I>, T: Config<I>, I: 'static> OnRuntimeUpgrade
209 for MigrateBlockNumberProvider<BlockConverter, T, I>
210 where
211 BlockConverter: BlockNumberConversion<SystemBlockNumberFor<T>, BlockNumberFor<T, I>>,
212 T: Config<I>,
213 {
214 #[cfg(feature = "try-runtime")]
215 fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
216 let referendum_count = v1::ReferendumInfoFor::<T, I>::iter().count();
217 log::info!(
218 target: TARGET,
219 "pre-upgrade state contains '{}' referendums.",
220 referendum_count
221 );
222 Ok((referendum_count as u32).encode())
223 }
224
225 fn on_runtime_upgrade() -> Weight {
226 let mut weight = Weight::zero();
227 weight.saturating_accrue(migrate_block_number_provider::<BlockConverter, T, I>());
228 weight
229 }
230
231 #[cfg(feature = "try-runtime")]
232 fn post_upgrade(state: Vec<u8>) -> Result<(), TryRuntimeError> {
233 let on_chain_version = Pallet::<T, I>::on_chain_storage_version();
234 ensure!(on_chain_version == 1, "must upgrade from version 1 to 2.");
235 let pre_referendum_count: u32 = Decode::decode(&mut &state[..])
236 .expect("failed to decode the state from pre-upgrade.");
237 let post_referendum_count = ReferendumInfoFor::<T, I>::iter().count() as u32;
238 ensure!(post_referendum_count == pre_referendum_count, "must migrate all referendums.");
239 log::info!(target: TARGET, "migrated all referendums.");
240 Ok(())
241 }
242 }
243
244 pub fn migrate_block_number_provider<BlockConverter, T, I: 'static>() -> Weight
245 where
246 BlockConverter: BlockNumberConversion<SystemBlockNumberFor<T>, BlockNumberFor<T, I>>,
247 T: Config<I>,
248 {
249 let in_code_version = Pallet::<T, I>::in_code_storage_version();
250 let on_chain_version = Pallet::<T, I>::on_chain_storage_version();
251 let mut weight = T::DbWeight::get().reads(1);
252 log::info!(
253 target: "runtime::referenda::migration::change_block_number_provider",
254 "running migration with in-code storage version {:?} / onchain {:?}.",
255 in_code_version,
256 on_chain_version
257 );
258 if on_chain_version == 0 {
259 log::error!(target: TARGET, "skipping migration from v0 to switch_block_number_provider.");
260 return weight;
261 }
262
263 v1::ReferendumInfoFor::<T, I>::iter().for_each(|(key, value)| {
265 let maybe_new_value = match value {
266 ReferendumInfo::Ongoing(_) | ReferendumInfo::Killed(_) => None,
267 ReferendumInfo::Approved(e, s, d) => {
268 let new_e = BlockConverter::convert_block_number(e);
269 Some(ReferendumInfo::Approved(new_e, s, d))
270 },
271 ReferendumInfo::Rejected(e, s, d) => {
272 let new_e = BlockConverter::convert_block_number(e);
273 Some(ReferendumInfo::Rejected(new_e, s, d))
274 },
275 ReferendumInfo::Cancelled(e, s, d) => {
276 let new_e = BlockConverter::convert_block_number(e);
277 Some(ReferendumInfo::Cancelled(new_e, s, d))
278 },
279 ReferendumInfo::TimedOut(e, s, d) => {
280 let new_e = BlockConverter::convert_block_number(e);
281 Some(ReferendumInfo::TimedOut(new_e, s, d))
282 },
283 };
284 if let Some(new_value) = maybe_new_value {
285 weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
286 log::info!(target: TARGET, "migrating referendum #{:?}", &key);
287 ReferendumInfoFor::<T, I>::insert(key, new_value);
288 } else {
289 weight.saturating_accrue(T::DbWeight::get().reads(1));
290 }
291 });
292
293 weight
294 }
295}
296
297#[cfg(test)]
298pub mod test {
299 use super::*;
300 use crate::{
301 migration::switch_block_number_provider::{
302 migrate_block_number_provider, BlockNumberConversion,
303 },
304 mock::{Test as T, *},
305 };
306 use core::str::FromStr;
307
308 fn create_status_v0() -> v0::ReferendumStatusOf<T, ()> {
310 let origin: OriginCaller = frame_system::RawOrigin::Root.into();
311 let track = <T as Config<()>>::Tracks::track_for(&origin).unwrap();
312 v0::ReferendumStatusOf::<T, ()> {
313 track,
314 in_queue: true,
315 origin,
316 proposal: set_balance_proposal_bounded(1),
317 enactment: DispatchTime::At(1),
318 tally: TallyOf::<T, ()>::new(track),
319 submission_deposit: Deposit { who: 1, amount: 10 },
320 submitted: 1,
321 decision_deposit: None,
322 alarm: None,
323 deciding: None,
324 }
325 }
326 #[test]
327 pub fn referendum_status_v0() {
328 let ongoing_encoded = sp_core::Bytes::from_str("0x00000000012c01082a0000000000000004000100000000000000010000000000000001000000000000000a00000000000000000000000000000000000100").unwrap();
330 let ongoing_dec = v0::ReferendumInfoOf::<T, ()>::decode(&mut &*ongoing_encoded).unwrap();
331 let ongoing = v0::ReferendumInfoOf::<T, ()>::Ongoing(create_status_v0());
332 assert_eq!(ongoing, ongoing_dec);
333 }
334
335 #[test]
336 fn migration_v0_to_v1_works() {
337 ExtBuilder::default().build_and_execute(|| {
338 let status_v0 = create_status_v0();
340 let ongoing_v0 = v0::ReferendumInfoOf::<T, ()>::Ongoing(status_v0.clone());
341 ReferendumCount::<T, ()>::mutate(|x| x.saturating_inc());
342 v0::ReferendumInfoFor::<T, ()>::insert(2, ongoing_v0);
343 let approved_v0 = v0::ReferendumInfoOf::<T, ()>::Approved(
345 123,
346 Deposit { who: 1, amount: 10 },
347 Some(Deposit { who: 2, amount: 20 }),
348 );
349 ReferendumCount::<T, ()>::mutate(|x| x.saturating_inc());
350 v0::ReferendumInfoFor::<T, ()>::insert(5, approved_v0);
351 v1::MigrateV0ToV1::<T, ()>::on_runtime_upgrade();
353 let ongoing_v1 = v1::ReferendumInfoFor::<T, ()>::get(2).unwrap();
355 assert_eq!(ReferendumInfoOf::<T, ()>::Ongoing(status_v0), ongoing_v1);
357 let approved_v1 = v1::ReferendumInfoFor::<T, ()>::get(5).unwrap();
359 assert_eq!(
360 approved_v1,
361 ReferendumInfoOf::<T, ()>::Approved(
362 123,
363 Some(Deposit { who: 1, amount: 10 }),
364 Some(Deposit { who: 2, amount: 20 })
365 )
366 );
367 });
368 }
369
370 #[test]
371 fn migration_v1_to_switch_block_number_provider_works() {
372 ExtBuilder::default().build_and_execute(|| {
373 pub struct MockBlockConverter;
374
375 impl BlockNumberConversion<SystemBlockNumberFor<T>, BlockNumberFor<T, ()>> for MockBlockConverter {
376 fn convert_block_number(block_number: SystemBlockNumberFor<T>) -> BlockNumberFor<T, ()> {
377 block_number as u64 + 10u64
378 }
379 }
380
381 let referendum_ongoing = v1::ReferendumInfoOf::<T, ()>::Ongoing(create_status_v0());
382 let referendum_approved = v1::ReferendumInfoOf::<T, ()>::Approved(
383 50, Some(Deposit { who: 1, amount: 10 }),
385 Some(Deposit { who: 2, amount: 20 }),
386 );
387
388 ReferendumCount::<T, ()>::mutate(|x| x.saturating_inc());
389 v1::ReferendumInfoFor::<T, ()>::insert(1, referendum_ongoing);
390
391 ReferendumCount::<T, ()>::mutate(|x| x.saturating_inc());
392 v1::ReferendumInfoFor::<T, ()>::insert(2, referendum_approved);
393
394 migrate_block_number_provider::<MockBlockConverter, T, ()>();
395
396 let ongoing_v2 = ReferendumInfoFor::<T, ()>::get(1).unwrap();
397 assert_eq!(
398 ongoing_v2,
399 ReferendumInfoOf::<T, ()>::Ongoing(create_status_v0())
400 );
401
402 let approved_v2 = ReferendumInfoFor::<T, ()>::get(2).unwrap();
403 assert_eq!(
404 approved_v2,
405 ReferendumInfoOf::<T, ()>::Approved(
406 50,
407 Some(Deposit { who: 1, amount: 10 }),
408 Some(Deposit { who: 2, amount: 20 })
409 )
410 );
411 });
412 }
413}