pallet_revive/migrations/
v2.rs1extern crate alloc;
25use super::PALLET_MIGRATIONS_ID;
26use crate::{vm::BytecodeType, weights::WeightInfo, Config, Pallet, H256, LOG_TARGET};
27use frame_support::{
28 migrations::{MigrationId, SteppedMigration, SteppedMigrationError},
29 pallet_prelude::PhantomData,
30 traits::{
31 fungible::{Inspect, Mutate, MutateHold},
32 tokens::{Fortitude, Precision, Restriction},
33 },
34 weights::WeightMeter,
35};
36
37#[cfg(feature = "try-runtime")]
38use alloc::{collections::btree_map::BTreeMap, vec::Vec};
39
40#[cfg(feature = "try-runtime")]
41use frame_support::{sp_runtime::TryRuntimeError, traits::fungible::InspectHold};
42
43mod old {
45 use super::Config;
46 use crate::{pallet::Pallet, AccountIdOf, BalanceOf, H256};
47 use codec::{Decode, Encode};
48 use frame_support::{storage_alias, Identity};
49
50 #[derive(Clone, Encode, Decode)]
51 pub struct CodeInfo<T: Config> {
52 pub owner: AccountIdOf<T>,
53 #[codec(compact)]
54 pub deposit: BalanceOf<T>,
55 #[codec(compact)]
56 pub refcount: u64,
57 pub code_len: u32,
58 pub behaviour_version: u32,
59 }
60
61 #[storage_alias]
62 pub type CodeInfoOf<T: Config> = StorageMap<Pallet<T>, Identity, H256, CodeInfo<T>>;
64}
65
66mod new {
67 use super::{BytecodeType, Config};
68 use crate::{pallet::Pallet, AccountIdOf, BalanceOf, H256};
69 use codec::{Decode, Encode};
70 use frame_support::{storage_alias, DebugNoBound, Identity};
71
72 #[derive(PartialEq, Eq, DebugNoBound, Encode, Decode)]
73 pub struct CodeInfo<T: Config> {
74 pub owner: AccountIdOf<T>,
75 #[codec(compact)]
76 pub deposit: BalanceOf<T>,
77 #[codec(compact)]
78 pub refcount: u64,
79 pub code_len: u32,
80 pub code_type: BytecodeType,
81 pub behaviour_version: u32,
82 }
83
84 #[storage_alias]
85 pub type CodeInfoOf<T: Config> = StorageMap<Pallet<T>, Identity, H256, CodeInfo<T>>;
87}
88
89pub struct Migration<T: Config>(PhantomData<T>);
92
93impl<T: Config> SteppedMigration for Migration<T> {
94 type Cursor = H256;
95 type Identifier = MigrationId<17>;
96
97 fn id() -> Self::Identifier {
98 MigrationId { pallet_id: *PALLET_MIGRATIONS_ID, version_from: 1, version_to: 2 }
99 }
100
101 fn step(
102 mut cursor: Option<Self::Cursor>,
103 meter: &mut WeightMeter,
104 ) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
105 let required = <T as Config>::WeightInfo::v2_migration_step();
106 if meter.remaining().any_lt(required) {
107 return Err(SteppedMigrationError::InsufficientWeight { required });
108 }
109
110 if !frame_system::Pallet::<T>::account_exists(&Pallet::<T>::account_id()) {
111 let _ =
112 T::Currency::mint_into(&Pallet::<T>::account_id(), T::Currency::minimum_balance());
113 }
114
115 loop {
116 if meter.try_consume(required).is_err() {
117 break;
118 }
119
120 let mut iter = if let Some(last_key) = cursor {
121 old::CodeInfoOf::<T>::iter_from(old::CodeInfoOf::<T>::hashed_key_for(last_key))
122 } else {
123 old::CodeInfoOf::<T>::iter()
124 };
125
126 if let Some((last_key, value)) = iter.next() {
127 if let Err(err) = T::Currency::transfer_on_hold(
128 &crate::HoldReason::CodeUploadDepositReserve.into(),
129 &value.owner,
130 &Pallet::<T>::account_id(),
131 value.deposit,
132 Precision::Exact,
133 Restriction::OnHold,
134 Fortitude::Polite,
135 ) {
136 log::error!(
137 target: LOG_TARGET,
138 "Failed to unhold the deposit for code hash {last_key:?} and owner {:?}: {err:?}",
139 value.owner,
140 );
141 }
142
143 new::CodeInfoOf::<T>::insert(
144 last_key,
145 new::CodeInfo {
146 owner: value.owner,
147 deposit: value.deposit,
148 refcount: value.refcount,
149 code_len: value.code_len,
150 code_type: BytecodeType::Pvm,
151 behaviour_version: value.behaviour_version,
152 },
153 );
154 cursor = Some(last_key)
155 } else {
156 cursor = None;
157 break
158 }
159 }
160 Ok(cursor)
161 }
162
163 #[cfg(feature = "try-runtime")]
164 fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
165 use codec::Encode;
166
167 Ok(old::CodeInfoOf::<T>::iter().collect::<BTreeMap<_, _>>().encode())
169 }
170
171 #[cfg(feature = "try-runtime")]
172 fn post_upgrade(prev: Vec<u8>) -> Result<(), TryRuntimeError> {
173 use codec::Decode;
174 use sp_runtime::{traits::Zero, Saturating};
175
176 let prev_map = BTreeMap::<H256, old::CodeInfo<T>>::decode(&mut &prev[..])
178 .expect("Failed to decode the previous storage state");
179
180 assert_eq!(
182 crate::CodeInfoOf::<T>::iter().count(),
183 prev_map.len(),
184 "Migration failed: the number of items in the storage after the migration is not the same as before"
185 );
186
187 let deposit_sum: crate::BalanceOf<T> = Zero::zero();
188
189 for (code_hash, old_code_info) in prev_map {
190 deposit_sum.saturating_add(old_code_info.deposit);
191 Self::assert_migrated_code_info(code_hash, &old_code_info);
192 }
193
194 assert_eq!(
195 <T as Config>::Currency::balance_on_hold(
196 &crate::HoldReason::CodeUploadDepositReserve.into(),
197 &Pallet::<T>::account_id(),
198 ),
199 deposit_sum,
200 );
201
202 Ok(())
203 }
204}
205
206#[cfg(any(feature = "runtime-benchmarks", feature = "try-runtime", test))]
207impl<T: Config> Migration<T> {
208 pub fn insert_old_code_info(code_hash: H256, code_info: old::CodeInfo<T>) {
210 old::CodeInfoOf::<T>::insert(code_hash, code_info);
211 }
212
213 pub fn create_old_code_info(
215 owner: crate::AccountIdOf<T>,
216 deposit: crate::BalanceOf<T>,
217 refcount: u64,
218 code_len: u32,
219 behaviour_version: u32,
220 ) -> old::CodeInfo<T> {
221 use frame_support::traits::fungible::Mutate;
222 T::Currency::mint_into(&owner, Pallet::<T>::min_balance() + deposit)
223 .expect("Failed to mint into owner account");
224 T::Currency::hold(&crate::HoldReason::CodeUploadDepositReserve.into(), &owner, deposit)
225 .expect("Failed to hold the deposit on the owner account");
226
227 old::CodeInfo { owner, deposit, refcount, code_len, behaviour_version }
228 }
229
230 pub fn assert_migrated_code_info(code_hash: H256, old_code_info: &old::CodeInfo<T>) {
232 use frame_support::traits::fungible::InspectHold;
233 use sp_runtime::traits::Zero;
234 let migrated =
235 new::CodeInfoOf::<T>::get(code_hash).expect("Failed to get migrated CodeInfo");
236
237 assert!(<T as Config>::Currency::balance_on_hold(
238 &crate::HoldReason::CodeUploadDepositReserve.into(),
239 &old_code_info.owner
240 )
241 .is_zero());
242
243 assert_eq!(
244 migrated,
245 new::CodeInfo {
246 owner: old_code_info.owner.clone(),
247 deposit: old_code_info.deposit,
248 refcount: old_code_info.refcount,
249 code_len: old_code_info.code_len,
250 behaviour_version: old_code_info.behaviour_version,
251 code_type: BytecodeType::Pvm,
252 },
253 "Migration failed: deposit mismatch for key {code_hash:?}",
254 );
255 }
256}
257
258#[test]
259fn migrate_to_v2() {
260 use crate::{
261 tests::{ExtBuilder, Test},
262 AccountIdOf,
263 };
264 use alloc::collections::BTreeMap;
265
266 ExtBuilder::default().genesis_config(None).build().execute_with(|| {
267 let mut original_values = BTreeMap::new();
269
270 for i in 0..10u8 {
271 let code_hash = H256::from([i; 32]);
272 let old_info = Migration::<Test>::create_old_code_info(
273 AccountIdOf::<Test>::from([i; 32]),
274 (1000u32 + i as u32).into(),
275 1 + i as u64,
276 100 + i as u32,
277 i as u32,
278 );
279
280 Migration::<Test>::insert_old_code_info(code_hash, old_info.clone());
281 original_values.insert(code_hash, old_info);
282 }
283
284 let mut cursor = None;
285 let mut weight_meter = WeightMeter::new();
286 while let Some(new_cursor) = Migration::<Test>::step(cursor, &mut weight_meter).unwrap() {
287 cursor = Some(new_cursor);
288 }
289
290 assert_eq!(crate::CodeInfoOf::<Test>::iter().count(), 10);
291
292 for (code_hash, old_value) in original_values {
294 Migration::<Test>::assert_migrated_code_info(code_hash, &old_value);
295 }
296 })
297}