pallet_contracts/migration/
v09.rs1use crate::{
21 migration::{IsFinished, MigrationStep},
22 weights::WeightInfo,
23 CodeHash, Config, Determinism, Pallet, Weight, LOG_TARGET,
24};
25use alloc::vec::Vec;
26use codec::{Decode, Encode};
27use frame_support::{
28 pallet_prelude::*, storage_alias, weights::WeightMeter, DefaultNoBound, Identity,
29};
30#[cfg(feature = "try-runtime")]
31use sp_runtime::TryRuntimeError;
32
33mod v8 {
34 use super::*;
35
36 #[derive(Encode, Decode)]
37 pub struct PrefabWasmModule {
38 #[codec(compact)]
39 pub instruction_weights_version: u32,
40 #[codec(compact)]
41 pub initial: u32,
42 #[codec(compact)]
43 pub maximum: u32,
44 pub code: Vec<u8>,
45 }
46
47 #[storage_alias]
48 pub type CodeStorage<T: Config> =
49 StorageMap<Pallet<T>, Identity, CodeHash<T>, PrefabWasmModule>;
50}
51
52#[cfg(feature = "runtime-benchmarks")]
53pub fn store_old_dummy_code<T: Config>(len: usize) {
54 use sp_runtime::traits::Hash;
55 let module = v8::PrefabWasmModule {
56 instruction_weights_version: 0,
57 initial: 0,
58 maximum: 0,
59 code: alloc::vec![42u8; len],
60 };
61 let hash = T::Hashing::hash(&module.code);
62 v8::CodeStorage::<T>::insert(hash, module);
63}
64
65#[derive(Encode, Decode)]
66struct PrefabWasmModule {
67 #[codec(compact)]
68 pub instruction_weights_version: u32,
69 #[codec(compact)]
70 pub initial: u32,
71 #[codec(compact)]
72 pub maximum: u32,
73 pub code: Vec<u8>,
74 pub determinism: Determinism,
75}
76
77#[storage_alias]
78type CodeStorage<T: Config> = StorageMap<Pallet<T>, Identity, CodeHash<T>, PrefabWasmModule>;
79
80#[derive(Encode, Decode, MaxEncodedLen, DefaultNoBound)]
81pub struct Migration<T: Config> {
82 last_code_hash: Option<CodeHash<T>>,
83}
84
85impl<T: Config> MigrationStep for Migration<T> {
86 const VERSION: u16 = 9;
87
88 fn max_step_weight() -> Weight {
89 T::WeightInfo::v9_migration_step(T::MaxCodeLen::get())
90 }
91
92 fn step(&mut self, meter: &mut WeightMeter) -> IsFinished {
93 let mut iter = if let Some(last_key) = self.last_code_hash.take() {
94 v8::CodeStorage::<T>::iter_from(v8::CodeStorage::<T>::hashed_key_for(last_key))
95 } else {
96 v8::CodeStorage::<T>::iter()
97 };
98
99 if let Some((key, old)) = iter.next() {
100 log::debug!(target: LOG_TARGET, "Migrating contract code {:?}", key);
101 let len = old.code.len() as u32;
102 let module = PrefabWasmModule {
103 instruction_weights_version: old.instruction_weights_version,
104 initial: old.initial,
105 maximum: old.maximum,
106 code: old.code,
107 determinism: Determinism::Enforced,
108 };
109 CodeStorage::<T>::insert(key, module);
110 self.last_code_hash = Some(key);
111 meter.consume(T::WeightInfo::v9_migration_step(len));
112 IsFinished::No
113 } else {
114 log::debug!(target: LOG_TARGET, "No more contracts code to migrate");
115 meter.consume(T::WeightInfo::v9_migration_step(0));
116 IsFinished::Yes
117 }
118 }
119
120 #[cfg(feature = "try-runtime")]
121 fn pre_upgrade_step() -> Result<Vec<u8>, TryRuntimeError> {
122 let sample: Vec<_> = v8::CodeStorage::<T>::iter().take(100).collect();
123
124 log::debug!(target: LOG_TARGET, "Taking sample of {} contract codes", sample.len());
125 Ok(sample.encode())
126 }
127
128 #[cfg(feature = "try-runtime")]
129 fn post_upgrade_step(state: Vec<u8>) -> Result<(), TryRuntimeError> {
130 let sample = <Vec<(CodeHash<T>, v8::PrefabWasmModule)> as Decode>::decode(&mut &state[..])
131 .expect("pre_upgrade_step provides a valid state; qed");
132
133 log::debug!(target: LOG_TARGET, "Validating sample of {} contract codes", sample.len());
134 for (code_hash, old) in sample {
135 let module = CodeStorage::<T>::get(&code_hash).unwrap();
136 ensure!(
137 module.instruction_weights_version == old.instruction_weights_version,
138 "invalid instruction weights version"
139 );
140 ensure!(module.determinism == Determinism::Enforced, "invalid determinism");
141 ensure!(module.initial == old.initial, "invalid initial");
142 ensure!(module.maximum == old.maximum, "invalid maximum");
143 ensure!(module.code == old.code, "invalid code");
144 }
145
146 Ok(())
147 }
148}