pallet_contracts/migration/
v14.rs1use crate::{
24 exec::AccountIdOf,
25 migration::{IsFinished, MigrationStep},
26 weights::WeightInfo,
27 BalanceOf, CodeHash, Config, Determinism, HoldReason, Pallet, Weight, LOG_TARGET,
28};
29#[cfg(feature = "try-runtime")]
30use alloc::collections::btree_map::BTreeMap;
31use codec::{Decode, Encode};
32#[cfg(feature = "try-runtime")]
33use environmental::Vec;
34#[cfg(feature = "try-runtime")]
35use frame_support::traits::fungible::{Inspect, InspectHold};
36use frame_support::{
37 pallet_prelude::*,
38 storage_alias,
39 traits::{fungible::MutateHold, ReservableCurrency},
40 weights::WeightMeter,
41 DefaultNoBound,
42};
43use sp_core::hexdisplay::HexDisplay;
44#[cfg(feature = "try-runtime")]
45use sp_runtime::TryRuntimeError;
46use sp_runtime::{traits::Zero, Saturating};
47
48mod v13 {
49 use super::*;
50
51 pub type BalanceOf<T, OldCurrency> = <OldCurrency as frame_support::traits::Currency<
52 <T as frame_system::Config>::AccountId,
53 >>::Balance;
54
55 #[derive(Encode, Decode, scale_info::TypeInfo, MaxEncodedLen)]
56 #[codec(mel_bound())]
57 #[scale_info(skip_type_params(T, OldCurrency))]
58 pub struct CodeInfo<T, OldCurrency>
59 where
60 T: Config,
61 OldCurrency: ReservableCurrency<<T as frame_system::Config>::AccountId>,
62 {
63 pub owner: AccountIdOf<T>,
64 #[codec(compact)]
65 pub deposit: v13::BalanceOf<T, OldCurrency>,
66 #[codec(compact)]
67 pub refcount: u64,
68 pub determinism: Determinism,
69 pub code_len: u32,
70 }
71
72 #[storage_alias]
73 pub type CodeInfoOf<T: Config, OldCurrency> =
74 StorageMap<Pallet<T>, Identity, CodeHash<T>, CodeInfo<T, OldCurrency>>;
75}
76
77#[cfg(feature = "runtime-benchmarks")]
78pub fn store_dummy_code<T: Config, OldCurrency>(account: T::AccountId)
79where
80 T: Config,
81 OldCurrency: ReservableCurrency<<T as frame_system::Config>::AccountId> + 'static,
82{
83 use alloc::vec;
84 use sp_runtime::traits::Hash;
85
86 let len = T::MaxCodeLen::get();
87 let code = vec![42u8; len as usize];
88 let hash = T::Hashing::hash(&code);
89
90 let info = v13::CodeInfo {
91 owner: account,
92 deposit: 10_000u32.into(),
93 refcount: u64::MAX,
94 determinism: Determinism::Enforced,
95 code_len: len,
96 };
97 v13::CodeInfoOf::<T, OldCurrency>::insert(hash, info);
98}
99
100#[cfg(feature = "try-runtime")]
101#[derive(Encode, Decode)]
102struct BalanceAllocation<T, OldCurrency>
104where
105 T: Config,
106 OldCurrency: ReservableCurrency<<T as frame_system::Config>::AccountId>,
107{
108 reserved: v13::BalanceOf<T, OldCurrency>,
110 total: v13::BalanceOf<T, OldCurrency>,
112}
113
114#[derive(Encode, Decode, MaxEncodedLen, DefaultNoBound)]
115pub struct Migration<T, OldCurrency>
116where
117 T: Config,
118 OldCurrency: ReservableCurrency<<T as frame_system::Config>::AccountId>,
119{
120 last_code_hash: Option<CodeHash<T>>,
121 _phantom: PhantomData<(T, OldCurrency)>,
122}
123
124impl<T, OldCurrency> MigrationStep for Migration<T, OldCurrency>
125where
126 T: Config,
127 OldCurrency: 'static + ReservableCurrency<<T as frame_system::Config>::AccountId>,
128 BalanceOf<T>: From<OldCurrency::Balance>,
129{
130 const VERSION: u16 = 14;
131
132 fn max_step_weight() -> Weight {
133 T::WeightInfo::v14_migration_step()
134 }
135
136 fn step(&mut self, meter: &mut WeightMeter) -> IsFinished {
137 let mut iter = if let Some(last_hash) = self.last_code_hash.take() {
138 v13::CodeInfoOf::<T, OldCurrency>::iter_from(
139 v13::CodeInfoOf::<T, OldCurrency>::hashed_key_for(last_hash),
140 )
141 } else {
142 v13::CodeInfoOf::<T, OldCurrency>::iter()
143 };
144
145 if let Some((hash, code_info)) = iter.next() {
146 log::debug!(target: LOG_TARGET, "Migrating code upload deposit for 0x{:?}", HexDisplay::from(&code_info.owner.encode()));
147
148 let remaining = OldCurrency::unreserve(&code_info.owner, code_info.deposit);
149
150 if remaining > Zero::zero() {
151 log::warn!(
152 target: LOG_TARGET,
153 "Code owner's account 0x{:?} for code {:?} has some non-unreservable deposit {:?} from a total of {:?} that will remain in reserved.",
154 HexDisplay::from(&code_info.owner.encode()),
155 hash,
156 remaining,
157 code_info.deposit
158 );
159 }
160
161 let unreserved = code_info.deposit.saturating_sub(remaining);
162 let amount = BalanceOf::<T>::from(unreserved);
163
164 log::debug!(
165 target: LOG_TARGET,
166 "Holding {:?} on the code owner's account 0x{:?} for code {:?}.",
167 amount,
168 HexDisplay::from(&code_info.owner.encode()),
169 hash,
170 );
171
172 T::Currency::hold(
173 &HoldReason::CodeUploadDepositReserve.into(),
174 &code_info.owner,
175 amount,
176 )
177 .unwrap_or_else(|err| {
178 log::error!(
179 target: LOG_TARGET,
180 "Failed to hold {:?} from the code owner's account 0x{:?} for code {:?}, reason: {:?}.",
181 amount,
182 HexDisplay::from(&code_info.owner.encode()),
183 hash,
184 err
185 );
186 });
187
188 self.last_code_hash = Some(hash);
189 meter.consume(T::WeightInfo::v14_migration_step());
190 IsFinished::No
191 } else {
192 log::debug!(target: LOG_TARGET, "No more code upload deposit to migrate");
193 meter.consume(T::WeightInfo::v14_migration_step());
194 IsFinished::Yes
195 }
196 }
197
198 #[cfg(feature = "try-runtime")]
199 fn pre_upgrade_step() -> Result<Vec<u8>, TryRuntimeError> {
200 let info: Vec<_> = v13::CodeInfoOf::<T, OldCurrency>::iter().collect();
201
202 let mut owner_balance_allocation =
203 BTreeMap::<AccountIdOf<T>, BalanceAllocation<T, OldCurrency>>::new();
204
205 for (_, code_info) in info {
208 owner_balance_allocation
209 .entry(code_info.owner.clone())
210 .and_modify(|alloc| {
211 alloc.reserved = alloc.reserved.saturating_add(code_info.deposit);
212 })
213 .or_insert(BalanceAllocation {
214 reserved: code_info.deposit,
215 total: OldCurrency::total_balance(&code_info.owner),
216 });
217 }
218
219 Ok(owner_balance_allocation.encode())
220 }
221
222 #[cfg(feature = "try-runtime")]
223 fn post_upgrade_step(state: Vec<u8>) -> Result<(), TryRuntimeError> {
224 let owner_balance_allocation =
225 <BTreeMap<AccountIdOf<T>, BalanceAllocation<T, OldCurrency>> as Decode>::decode(
226 &mut &state[..],
227 )
228 .expect("pre_upgrade_step provides a valid state; qed");
229
230 let mut total_held: BalanceOf<T> = Zero::zero();
231 let count = owner_balance_allocation.len();
232 for (owner, old_balance_allocation) in owner_balance_allocation {
233 let held =
234 T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &owner);
235 log::debug!(
236 target: LOG_TARGET,
237 "Validating code upload deposit for owner 0x{:?}, reserved: {:?}, held: {:?}",
238 HexDisplay::from(&owner.encode()),
239 old_balance_allocation.reserved,
240 held
241 );
242 ensure!(held == old_balance_allocation.reserved.into(), "Held amount mismatch");
243
244 log::debug!(
245 target: LOG_TARGET,
246 "Validating total balance for owner 0x{:?}, new: {:?}, old: {:?}",
247 HexDisplay::from(&owner.encode()),
248 T::Currency::total_balance(&owner),
249 old_balance_allocation.total
250 );
251 ensure!(
252 T::Currency::total_balance(&owner) ==
253 BalanceOf::<T>::decode(&mut &old_balance_allocation.total.encode()[..])
254 .unwrap(),
255 "Balance mismatch "
256 );
257 total_held += held;
258 }
259
260 log::info!(
261 target: LOG_TARGET,
262 "Code owners processed: {:?}.",
263 count
264 );
265
266 log::info!(
267 target: LOG_TARGET,
268 "Total held amount for code upload deposit: {:?}",
269 total_held
270 );
271
272 Ok(())
273 }
274}