1pub use v1::MigrateToV1;
15
16pub mod v0 {
17 use crate::inclusion::{Config, Pallet};
18 use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec};
19 use codec::{Decode, Encode};
20 use frame_support::{storage_alias, Twox64Concat};
21 use frame_system::pallet_prelude::BlockNumberFor;
22 use polkadot_primitives::{
23 AvailabilityBitfield, CandidateCommitments, CandidateDescriptorV2 as CandidateDescriptor,
24 CandidateHash, CoreIndex, GroupIndex, Id as ParaId, ValidatorIndex,
25 };
26 use scale_info::TypeInfo;
27
28 #[derive(Encode, Decode, PartialEq, TypeInfo, Clone, Debug)]
29 pub struct CandidatePendingAvailability<H, N> {
30 pub core: CoreIndex,
31 pub hash: CandidateHash,
32 pub descriptor: CandidateDescriptor<H>,
33 pub availability_votes: BitVec<u8, BitOrderLsb0>,
34 pub backers: BitVec<u8, BitOrderLsb0>,
35 pub relay_parent_number: N,
36 pub backed_in_number: N,
37 pub backing_group: GroupIndex,
38 }
39
40 #[derive(Encode, Decode, TypeInfo, Debug, PartialEq)]
41 pub struct AvailabilityBitfieldRecord<N> {
42 pub bitfield: AvailabilityBitfield,
43 pub submitted_at: N,
44 }
45
46 #[storage_alias]
47 pub type PendingAvailability<T: Config> = StorageMap<
48 Pallet<T>,
49 Twox64Concat,
50 ParaId,
51 CandidatePendingAvailability<<T as frame_system::Config>::Hash, BlockNumberFor<T>>,
52 >;
53
54 #[storage_alias]
55 pub type PendingAvailabilityCommitments<T: Config> =
56 StorageMap<Pallet<T>, Twox64Concat, ParaId, CandidateCommitments>;
57
58 #[storage_alias]
59 pub type AvailabilityBitfields<T: Config> = StorageMap<
60 Pallet<T>,
61 Twox64Concat,
62 ValidatorIndex,
63 AvailabilityBitfieldRecord<BlockNumberFor<T>>,
64 >;
65}
66
67mod v1 {
68 use super::v0::{
69 AvailabilityBitfields, PendingAvailability as V0PendingAvailability,
70 PendingAvailabilityCommitments as V0PendingAvailabilityCommitments,
71 };
72 use crate::inclusion::{
73 CandidatePendingAvailability as V1CandidatePendingAvailability, Config, Pallet,
74 PendingAvailability as V1PendingAvailability,
75 };
76 use alloc::{collections::vec_deque::VecDeque, vec::Vec};
77 use frame_support::{traits::UncheckedOnRuntimeUpgrade, weights::Weight};
78 use sp_core::Get;
79
80 #[cfg(feature = "try-runtime")]
81 use codec::{Decode, Encode};
82 #[cfg(feature = "try-runtime")]
83 use frame_support::{
84 ensure,
85 traits::{GetStorageVersion, StorageVersion},
86 };
87
88 pub struct VersionUncheckedMigrateToV1<T>(core::marker::PhantomData<T>);
89
90 impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateToV1<T> {
91 #[cfg(feature = "try-runtime")]
92 fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
93 log::trace!(target: crate::inclusion::LOG_TARGET, "Running pre_upgrade() for inclusion MigrateToV1");
94 let candidates_before_upgrade = V0PendingAvailability::<T>::iter().count();
95 let commitments_before_upgrade = V0PendingAvailabilityCommitments::<T>::iter().count();
96
97 if candidates_before_upgrade != commitments_before_upgrade {
98 log::warn!(
99 target: crate::inclusion::LOG_TARGET,
100 "Number of pending candidates differ from the number of pending commitments. {} vs {}",
101 candidates_before_upgrade,
102 commitments_before_upgrade
103 );
104 }
105
106 Ok((candidates_before_upgrade as u32).encode())
107 }
108
109 fn on_runtime_upgrade() -> Weight {
110 let mut weight: Weight = Weight::zero();
111
112 let v0_candidates: Vec<_> = V0PendingAvailability::<T>::drain().collect();
113
114 for (para_id, candidate) in v0_candidates {
115 let commitments = V0PendingAvailabilityCommitments::<T>::take(para_id);
116 weight = weight.saturating_add(T::DbWeight::get().writes(2));
118
119 if let Some(commitments) = commitments {
120 let mut per_para = VecDeque::new();
121 per_para.push_back(V1CandidatePendingAvailability {
122 core: candidate.core,
123 hash: candidate.hash,
124 descriptor: candidate.descriptor,
125 availability_votes: candidate.availability_votes,
126 backers: candidate.backers,
127 relay_parent_number: candidate.relay_parent_number,
128 backed_in_number: candidate.backed_in_number,
129 backing_group: candidate.backing_group,
130 commitments,
131 });
132 V1PendingAvailability::<T>::insert(para_id, per_para);
133
134 weight = weight.saturating_add(T::DbWeight::get().writes(1));
135 }
136 }
137
138 let res = V0PendingAvailabilityCommitments::<T>::clear(u32::MAX, None);
143 weight = weight.saturating_add(
144 T::DbWeight::get().reads_writes(res.loops as u64, res.backend as u64),
145 );
146
147 let res = AvailabilityBitfields::<T>::clear(u32::MAX, None);
150 weight = weight.saturating_add(
151 T::DbWeight::get().reads_writes(res.loops as u64, res.backend as u64),
152 );
153
154 weight
155 }
156
157 #[cfg(feature = "try-runtime")]
158 fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
159 log::trace!(target: crate::inclusion::LOG_TARGET, "Running post_upgrade() for inclusion MigrateToV1");
160 ensure!(
161 Pallet::<T>::on_chain_storage_version() >= StorageVersion::new(1),
162 "Storage version should be >= 1 after the migration"
163 );
164
165 let candidates_before_upgrade =
166 u32::decode(&mut &state[..]).expect("Was properly encoded") as usize;
167 let candidates_after_upgrade = V1PendingAvailability::<T>::iter().fold(
168 0usize,
169 |mut acc, (_paraid, para_candidates)| {
170 acc += para_candidates.len();
171 acc
172 },
173 );
174
175 ensure!(
176 candidates_before_upgrade == candidates_after_upgrade,
177 "Number of pending candidates should be the same as the one before the upgrade."
178 );
179 ensure!(
180 V0PendingAvailability::<T>::iter().next() == None,
181 "Pending availability candidates storage v0 should have been removed"
182 );
183 ensure!(
184 V0PendingAvailabilityCommitments::<T>::iter().next() == None,
185 "Pending availability commitments storage should have been removed"
186 );
187 ensure!(
188 AvailabilityBitfields::<T>::iter().next() == None,
189 "Availability bitfields storage should have been removed"
190 );
191
192 Ok(())
193 }
194 }
195
196 pub type MigrateToV1<T> = frame_support::migrations::VersionedMigration<
201 0,
202 1,
203 VersionUncheckedMigrateToV1<T>,
204 Pallet<T>,
205 <T as frame_system::Config>::DbWeight,
206 >;
207}
208
209#[cfg(test)]
210mod tests {
211 use super::{v1::VersionUncheckedMigrateToV1, *};
212 use crate::{
213 inclusion::{
214 CandidatePendingAvailability as V1CandidatePendingAvailability,
215 PendingAvailability as V1PendingAvailability, *,
216 },
217 mock::{new_test_ext, MockGenesisConfig, Test},
218 };
219 use frame_support::traits::UncheckedOnRuntimeUpgrade;
220 use polkadot_primitives::{AvailabilityBitfield, Id as ParaId};
221 use polkadot_primitives_test_helpers::{
222 dummy_candidate_commitments, dummy_candidate_descriptor_v2, dummy_hash,
223 };
224
225 #[test]
226 fn migrate_to_v1() {
227 new_test_ext(MockGenesisConfig::default()).execute_with(|| {
228 assert_eq!(
230 <VersionUncheckedMigrateToV1<Test> as UncheckedOnRuntimeUpgrade>::on_runtime_upgrade(),
231 Weight::zero()
232 );
233 assert!(V1PendingAvailability::<Test>::iter().next().is_none());
234
235 let mut expected = vec![];
236
237 for i in 1..5 {
238 let descriptor = dummy_candidate_descriptor_v2(dummy_hash());
239 v0::PendingAvailability::<Test>::insert(
240 ParaId::from(i),
241 v0::CandidatePendingAvailability {
242 core: CoreIndex(i),
243 descriptor: descriptor.clone(),
244 relay_parent_number: i,
245 hash: CandidateHash(dummy_hash()),
246 availability_votes: Default::default(),
247 backed_in_number: i,
248 backers: Default::default(),
249 backing_group: GroupIndex(i),
250 },
251 );
252 v0::PendingAvailabilityCommitments::<Test>::insert(
253 ParaId::from(i),
254 dummy_candidate_commitments(HeadData(vec![i as _])),
255 );
256
257 v0::AvailabilityBitfields::<Test>::insert(
258 ValidatorIndex(i),
259 v0::AvailabilityBitfieldRecord {
260 bitfield: AvailabilityBitfield(Default::default()),
261 submitted_at: i,
262 },
263 );
264
265 expected.push((
266 ParaId::from(i),
267 [V1CandidatePendingAvailability {
268 core: CoreIndex(i),
269 descriptor,
270 relay_parent_number: i,
271 hash: CandidateHash(dummy_hash()),
272 availability_votes: Default::default(),
273 backed_in_number: i,
274 backers: Default::default(),
275 backing_group: GroupIndex(i),
276 commitments: dummy_candidate_commitments(HeadData(vec![i as _])),
277 }]
278 .into_iter()
279 .collect::<VecDeque<_>>(),
280 ));
281 }
282 v0::PendingAvailability::<Test>::insert(
285 ParaId::from(6),
286 v0::CandidatePendingAvailability {
287 core: CoreIndex(6),
288 descriptor: dummy_candidate_descriptor_v2(dummy_hash()),
289 relay_parent_number: 6,
290 hash: CandidateHash(dummy_hash()),
291 availability_votes: Default::default(),
292 backed_in_number: 6,
293 backers: Default::default(),
294 backing_group: GroupIndex(6),
295 },
296 );
297 v0::PendingAvailabilityCommitments::<Test>::insert(
298 ParaId::from(7),
299 dummy_candidate_commitments(HeadData(vec![7 as _])),
300 );
301
302 assert_eq!(
304 <VersionUncheckedMigrateToV1<Test> as UncheckedOnRuntimeUpgrade>::on_runtime_upgrade(),
305 Weight::zero()
306 );
307
308 assert_eq!(v0::PendingAvailabilityCommitments::<Test>::iter().next(), None);
309 assert_eq!(v0::PendingAvailability::<Test>::iter().next(), None);
310 assert_eq!(v0::AvailabilityBitfields::<Test>::iter().next(), None);
311
312 let mut actual = V1PendingAvailability::<Test>::iter().collect::<Vec<_>>();
313 actual.sort_by(|(id1, _), (id2, _)| id1.cmp(id2));
314 expected.sort_by(|(id1, _), (id2, _)| id1.cmp(id2));
315
316 assert_eq!(actual, expected);
317 });
318 }
319}