referrerpolicy=no-referrer-when-downgrade

pallet_democracy/migrations/
v1.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Storage migrations for the preimage pallet.
19
20use crate::*;
21use frame_support::{pallet_prelude::*, storage_alias, traits::OnRuntimeUpgrade, BoundedVec};
22use frame_system::pallet_prelude::BlockNumberFor;
23use sp_core::H256;
24
25/// The log target.
26const TARGET: &'static str = "runtime::democracy::migration::v1";
27
28/// The original data layout of the democracy pallet without a specific version number.
29mod v0 {
30	use super::*;
31
32	#[storage_alias]
33	pub type PublicProps<T: Config> = StorageValue<
34		Pallet<T>,
35		Vec<(PropIndex, <T as frame_system::Config>::Hash, <T as frame_system::Config>::AccountId)>,
36		ValueQuery,
37	>;
38
39	#[storage_alias]
40	pub type NextExternal<T: Config> =
41		StorageValue<Pallet<T>, (<T as frame_system::Config>::Hash, VoteThreshold)>;
42
43	#[cfg(feature = "try-runtime")]
44	#[storage_alias]
45	pub type ReferendumInfoOf<T: Config> = StorageMap<
46		Pallet<T>,
47		frame_support::Twox64Concat,
48		ReferendumIndex,
49		ReferendumInfo<BlockNumberFor<T>, <T as frame_system::Config>::Hash, BalanceOf<T>>,
50	>;
51}
52
53pub mod v1 {
54	use super::*;
55
56	/// Migration for translating bare `Hash`es into `Bounded<Call>`s.
57	pub struct Migration<T>(core::marker::PhantomData<T>);
58
59	impl<T: Config + frame_system::Config<Hash = H256>> OnRuntimeUpgrade for Migration<T> {
60		#[cfg(feature = "try-runtime")]
61		fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
62			ensure!(StorageVersion::get::<Pallet<T>>() == 0, "can only upgrade from version 0");
63
64			let props_count = v0::PublicProps::<T>::get().len();
65			log::info!(target: TARGET, "{} public proposals will be migrated.", props_count,);
66			ensure!(props_count <= T::MaxProposals::get() as usize, Error::<T>::TooMany);
67
68			let referenda_count = v0::ReferendumInfoOf::<T>::iter().count();
69			log::info!(target: TARGET, "{} referenda will be migrated.", referenda_count);
70
71			Ok((props_count as u32, referenda_count as u32).encode())
72		}
73
74		#[allow(deprecated)]
75		fn on_runtime_upgrade() -> Weight {
76			let mut weight = T::DbWeight::get().reads(1);
77			if StorageVersion::get::<Pallet<T>>() != 0 {
78				log::warn!(
79					target: TARGET,
80					"skipping on_runtime_upgrade: executed on wrong storage version.\
81				Expected version 0"
82				);
83				return weight;
84			}
85
86			ReferendumInfoOf::<T>::translate(
87				|index, old: ReferendumInfo<BlockNumberFor<T>, T::Hash, BalanceOf<T>>| {
88					weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
89					log::info!(target: TARGET, "migrating referendum #{:?}", &index);
90					Some(match old {
91						ReferendumInfo::Ongoing(status) => {
92							ReferendumInfo::Ongoing(ReferendumStatus {
93								end: status.end,
94								proposal: Bounded::from_legacy_hash(status.proposal),
95								threshold: status.threshold,
96								delay: status.delay,
97								tally: status.tally,
98							})
99						},
100						ReferendumInfo::Finished { approved, end } => {
101							ReferendumInfo::Finished { approved, end }
102						},
103					})
104				},
105			);
106
107			let props = v0::PublicProps::<T>::take()
108				.into_iter()
109				.map(|(i, hash, a)| (i, Bounded::from_legacy_hash(hash), a))
110				.collect::<Vec<_>>();
111			let bounded = BoundedVec::<_, T::MaxProposals>::truncate_from(props.clone());
112			PublicProps::<T>::put(bounded);
113			weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
114
115			if props.len() as u32 > T::MaxProposals::get() {
116				log::error!(
117					target: TARGET,
118					"truncated {} public proposals to {}; continuing",
119					props.len(),
120					T::MaxProposals::get()
121				);
122			}
123
124			if let Some((hash, threshold)) = v0::NextExternal::<T>::take() {
125				log::info!(target: TARGET, "migrating next external proposal");
126				NextExternal::<T>::put((Bounded::from_legacy_hash(hash), threshold));
127			}
128
129			StorageVersion::new(1).put::<Pallet<T>>();
130
131			weight.saturating_add(T::DbWeight::get().reads_writes(1, 3))
132		}
133
134		#[cfg(feature = "try-runtime")]
135		fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
136			ensure!(StorageVersion::get::<Pallet<T>>() == 1, "must upgrade");
137
138			let (old_props_count, old_ref_count): (u32, u32) =
139				Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed");
140			let new_props_count = crate::PublicProps::<T>::get().len() as u32;
141			ensure!(new_props_count == old_props_count, "must migrate all public proposals");
142			let new_ref_count = crate::ReferendumInfoOf::<T>::iter().count() as u32;
143			ensure!(new_ref_count == old_ref_count, "must migrate all referenda");
144
145			log::info!(
146				target: TARGET,
147				"{} public proposals migrated, {} referenda migrated",
148				new_props_count,
149				new_ref_count,
150			);
151			Ok(())
152		}
153	}
154}
155
156#[cfg(test)]
157#[cfg(feature = "try-runtime")]
158mod test {
159	use super::*;
160	use crate::{
161		tests::{Test as T, *},
162		types::*,
163	};
164	use sp_runtime::bounded_vec;
165
166	#[allow(deprecated)]
167	#[test]
168	fn migration_works() {
169		new_test_ext().execute_with(|| {
170			assert_eq!(StorageVersion::get::<Pallet<T>>(), 0);
171			// Insert some values into the v0 storage:
172
173			// Case 1: Ongoing referendum
174			let hash = H256::repeat_byte(1);
175			let status = ReferendumStatus {
176				end: 1u32.into(),
177				proposal: hash,
178				threshold: VoteThreshold::SuperMajorityApprove,
179				delay: 1u32.into(),
180				tally: Tally { ayes: 1u32.into(), nays: 1u32.into(), turnout: 1u32.into() },
181			};
182			v0::ReferendumInfoOf::<T>::insert(1u32, ReferendumInfo::Ongoing(status));
183
184			// Case 2: Finished referendum
185			v0::ReferendumInfoOf::<T>::insert(
186				2u32,
187				ReferendumInfo::Finished { approved: true, end: 123u32.into() },
188			);
189
190			// Case 3: Public proposals
191			let hash2 = H256::repeat_byte(2);
192			v0::PublicProps::<T>::put(vec![(3u32, hash, 123u64), (4u32, hash2, 123u64)]);
193
194			// Case 4: Next external
195			v0::NextExternal::<T>::put((hash, VoteThreshold::SuperMajorityApprove));
196
197			// Migrate.
198			let state = v1::Migration::<T>::pre_upgrade().unwrap();
199			let _weight = v1::Migration::<T>::on_runtime_upgrade();
200			v1::Migration::<T>::post_upgrade(state).unwrap();
201			// Check that all values got migrated.
202
203			// Case 1: Ongoing referendum
204			assert_eq!(
205				ReferendumInfoOf::<T>::get(1u32),
206				Some(ReferendumInfo::Ongoing(ReferendumStatus {
207					end: 1u32.into(),
208					proposal: Bounded::from_legacy_hash(hash),
209					threshold: VoteThreshold::SuperMajorityApprove,
210					delay: 1u32.into(),
211					tally: Tally { ayes: 1u32.into(), nays: 1u32.into(), turnout: 1u32.into() },
212				}))
213			);
214			// Case 2: Finished referendum
215			assert_eq!(
216				ReferendumInfoOf::<T>::get(2u32),
217				Some(ReferendumInfo::Finished { approved: true, end: 123u32.into() })
218			);
219			// Case 3: Public proposals
220			let props: BoundedVec<_, <Test as Config>::MaxProposals> = bounded_vec![
221				(3u32, Bounded::from_legacy_hash(hash), 123u64),
222				(4u32, Bounded::from_legacy_hash(hash2), 123u64)
223			];
224			assert_eq!(PublicProps::<T>::get(), props);
225			// Case 4: Next external
226			assert_eq!(
227				NextExternal::<T>::get(),
228				Some((Bounded::from_legacy_hash(hash), VoteThreshold::SuperMajorityApprove))
229			);
230		});
231	}
232}