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						ReferendumInfo::Finished { approved, end } =>
100							ReferendumInfo::Finished { approved, end },
101					})
102				},
103			);
104
105			let props = v0::PublicProps::<T>::take()
106				.into_iter()
107				.map(|(i, hash, a)| (i, Bounded::from_legacy_hash(hash), a))
108				.collect::<Vec<_>>();
109			let bounded = BoundedVec::<_, T::MaxProposals>::truncate_from(props.clone());
110			PublicProps::<T>::put(bounded);
111			weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
112
113			if props.len() as u32 > T::MaxProposals::get() {
114				log::error!(
115					target: TARGET,
116					"truncated {} public proposals to {}; continuing",
117					props.len(),
118					T::MaxProposals::get()
119				);
120			}
121
122			if let Some((hash, threshold)) = v0::NextExternal::<T>::take() {
123				log::info!(target: TARGET, "migrating next external proposal");
124				NextExternal::<T>::put((Bounded::from_legacy_hash(hash), threshold));
125			}
126
127			StorageVersion::new(1).put::<Pallet<T>>();
128
129			weight.saturating_add(T::DbWeight::get().reads_writes(1, 3))
130		}
131
132		#[cfg(feature = "try-runtime")]
133		fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
134			ensure!(StorageVersion::get::<Pallet<T>>() == 1, "must upgrade");
135
136			let (old_props_count, old_ref_count): (u32, u32) =
137				Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed");
138			let new_props_count = crate::PublicProps::<T>::get().len() as u32;
139			ensure!(new_props_count == old_props_count, "must migrate all public proposals");
140			let new_ref_count = crate::ReferendumInfoOf::<T>::iter().count() as u32;
141			ensure!(new_ref_count == old_ref_count, "must migrate all referenda");
142
143			log::info!(
144				target: TARGET,
145				"{} public proposals migrated, {} referenda migrated",
146				new_props_count,
147				new_ref_count,
148			);
149			Ok(())
150		}
151	}
152}
153
154#[cfg(test)]
155#[cfg(feature = "try-runtime")]
156mod test {
157	use super::*;
158	use crate::{
159		tests::{Test as T, *},
160		types::*,
161	};
162	use sp_runtime::bounded_vec;
163
164	#[allow(deprecated)]
165	#[test]
166	fn migration_works() {
167		new_test_ext().execute_with(|| {
168			assert_eq!(StorageVersion::get::<Pallet<T>>(), 0);
169			// Insert some values into the v0 storage:
170
171			// Case 1: Ongoing referendum
172			let hash = H256::repeat_byte(1);
173			let status = ReferendumStatus {
174				end: 1u32.into(),
175				proposal: hash,
176				threshold: VoteThreshold::SuperMajorityApprove,
177				delay: 1u32.into(),
178				tally: Tally { ayes: 1u32.into(), nays: 1u32.into(), turnout: 1u32.into() },
179			};
180			v0::ReferendumInfoOf::<T>::insert(1u32, ReferendumInfo::Ongoing(status));
181
182			// Case 2: Finished referendum
183			v0::ReferendumInfoOf::<T>::insert(
184				2u32,
185				ReferendumInfo::Finished { approved: true, end: 123u32.into() },
186			);
187
188			// Case 3: Public proposals
189			let hash2 = H256::repeat_byte(2);
190			v0::PublicProps::<T>::put(vec![(3u32, hash, 123u64), (4u32, hash2, 123u64)]);
191
192			// Case 4: Next external
193			v0::NextExternal::<T>::put((hash, VoteThreshold::SuperMajorityApprove));
194
195			// Migrate.
196			let state = v1::Migration::<T>::pre_upgrade().unwrap();
197			let _weight = v1::Migration::<T>::on_runtime_upgrade();
198			v1::Migration::<T>::post_upgrade(state).unwrap();
199			// Check that all values got migrated.
200
201			// Case 1: Ongoing referendum
202			assert_eq!(
203				ReferendumInfoOf::<T>::get(1u32),
204				Some(ReferendumInfo::Ongoing(ReferendumStatus {
205					end: 1u32.into(),
206					proposal: Bounded::from_legacy_hash(hash),
207					threshold: VoteThreshold::SuperMajorityApprove,
208					delay: 1u32.into(),
209					tally: Tally { ayes: 1u32.into(), nays: 1u32.into(), turnout: 1u32.into() },
210				}))
211			);
212			// Case 2: Finished referendum
213			assert_eq!(
214				ReferendumInfoOf::<T>::get(2u32),
215				Some(ReferendumInfo::Finished { approved: true, end: 123u32.into() })
216			);
217			// Case 3: Public proposals
218			let props: BoundedVec<_, <Test as Config>::MaxProposals> = bounded_vec![
219				(3u32, Bounded::from_legacy_hash(hash), 123u64),
220				(4u32, Bounded::from_legacy_hash(hash2), 123u64)
221			];
222			assert_eq!(PublicProps::<T>::get(), props);
223			// Case 4: Next external
224			assert_eq!(
225				NextExternal::<T>::get(),
226				Some((Bounded::from_legacy_hash(hash), VoteThreshold::SuperMajorityApprove))
227			);
228		});
229	}
230}