// This file is part of Substrate. // Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use crate::{BalanceOf, CodeHash, Config, Pallet, TrieId, Weight}; use codec::{Decode, Encode}; use frame_support::{ codec, pallet_prelude::*, storage::migration, storage_alias, traits::{Get, OnRuntimeUpgrade}, Identity, Twox64Concat, }; use sp_runtime::traits::Saturating; use sp_std::{marker::PhantomData, prelude::*}; /// Performs all necessary migrations based on `StorageVersion`. pub struct Migration(PhantomData); impl OnRuntimeUpgrade for Migration { fn on_runtime_upgrade() -> Weight { let version = >::on_chain_storage_version(); let mut weight = Weight::zero(); if version < 4 { v4::migrate::(&mut weight); } if version < 5 { v5::migrate::(&mut weight); } if version < 6 { v6::migrate::(&mut weight); } if version < 7 { v7::migrate::(&mut weight); } if version < 8 { v8::migrate::(&mut weight); } if version < 9 { v9::migrate::(&mut weight); } StorageVersion::new(9).put::>(); weight.saturating_accrue(T::DbWeight::get().writes(1)); weight } #[cfg(feature = "try-runtime")] fn pre_upgrade() -> Result, &'static str> { let version = >::on_chain_storage_version(); if version == 7 { v8::pre_upgrade::()?; } Ok(version.encode()) } #[cfg(feature = "try-runtime")] fn post_upgrade(state: Vec) -> Result<(), &'static str> { let version = Decode::decode(&mut state.as_ref()).map_err(|_| "Cannot decode version")?; post_checks::post_upgrade::(version) } } /// V4: `Schedule` is changed to be a config item rather than an in-storage value. mod v4 { use super::*; pub fn migrate(weight: &mut Weight) { #[allow(deprecated)] migration::remove_storage_prefix(>::name().as_bytes(), b"CurrentSchedule", b""); weight.saturating_accrue(T::DbWeight::get().writes(1)); } } /// V5: State rent is removed which obsoletes some fields in `ContractInfo`. mod v5 { use super::*; type AliveContractInfo = RawAliveContractInfo, BalanceOf, ::BlockNumber>; type TombstoneContractInfo = RawTombstoneContractInfo< ::Hash, ::Hashing, >; #[derive(Decode)] enum OldContractInfo { Alive(AliveContractInfo), Tombstone(TombstoneContractInfo), } #[derive(Decode)] struct RawAliveContractInfo { trie_id: TrieId, _storage_size: u32, _pair_count: u32, code_hash: CodeHash, _rent_allowance: Balance, _rent_paid: Balance, _deduct_block: BlockNumber, _last_write: Option, _reserved: Option<()>, } #[derive(Decode)] struct RawTombstoneContractInfo(H, PhantomData); #[derive(Decode)] struct OldDeletedContract { _pair_count: u32, trie_id: TrieId, } pub type ContractInfo = RawContractInfo>; #[derive(Encode, Decode)] pub struct RawContractInfo { pub trie_id: TrieId, pub code_hash: CodeHash, pub _reserved: Option<()>, } #[derive(Encode, Decode)] struct DeletedContract { trie_id: TrieId, } #[storage_alias] type ContractInfoOf = StorageMap< Pallet, Twox64Concat, ::AccountId, ContractInfo, >; #[storage_alias] type DeletionQueue = StorageValue, Vec>; pub fn migrate(weight: &mut Weight) { >::translate(|_key, old: OldContractInfo| { weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); match old { OldContractInfo::Alive(old) => Some(ContractInfo:: { trie_id: old.trie_id, code_hash: old.code_hash, _reserved: old._reserved, }), OldContractInfo::Tombstone(_) => None, } }); DeletionQueue::::translate(|old: Option>| { weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); old.map(|old| old.into_iter().map(|o| DeletedContract { trie_id: o.trie_id }).collect()) }) .ok(); } } /// V6: Added storage deposits mod v6 { use super::*; #[derive(Encode, Decode)] struct OldPrefabWasmModule { #[codec(compact)] instruction_weights_version: u32, #[codec(compact)] initial: u32, #[codec(compact)] maximum: u32, #[codec(compact)] refcount: u64, _reserved: Option<()>, code: Vec, original_code_len: u32, } #[derive(Encode, Decode)] pub struct PrefabWasmModule { #[codec(compact)] pub instruction_weights_version: u32, #[codec(compact)] pub initial: u32, #[codec(compact)] pub maximum: u32, pub code: Vec, } use v5::ContractInfo as OldContractInfo; #[derive(Encode, Decode)] pub struct RawContractInfo { pub trie_id: TrieId, pub code_hash: CodeHash, pub storage_deposit: Balance, } #[derive(Encode, Decode)] pub struct OwnerInfo { owner: T::AccountId, #[codec(compact)] deposit: BalanceOf, #[codec(compact)] refcount: u64, } pub type ContractInfo = RawContractInfo, BalanceOf>; #[storage_alias] type ContractInfoOf = StorageMap< Pallet, Twox64Concat, ::AccountId, ContractInfo, >; #[storage_alias] type CodeStorage = StorageMap, Identity, CodeHash, PrefabWasmModule>; #[storage_alias] type OwnerInfoOf = StorageMap, Identity, CodeHash, OwnerInfo>; pub fn migrate(weight: &mut Weight) { >::translate(|_key, old: OldContractInfo| { weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); Some(ContractInfo:: { trie_id: old.trie_id, code_hash: old.code_hash, storage_deposit: Default::default(), }) }); let nobody = T::AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()) .expect("Infinite input; no dead input space; qed"); >::translate(|key, old: OldPrefabWasmModule| { weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); >::insert( key, OwnerInfo { refcount: old.refcount, owner: nobody.clone(), deposit: Default::default(), }, ); Some(PrefabWasmModule { instruction_weights_version: old.instruction_weights_version, initial: old.initial, maximum: old.maximum, code: old.code, }) }); } } /// Rename `AccountCounter` to `Nonce`. mod v7 { use super::*; pub fn migrate(weight: &mut Weight) { #[storage_alias] type AccountCounter = StorageValue, u64, ValueQuery>; #[storage_alias] type Nonce = StorageValue, u64, ValueQuery>; Nonce::::set(AccountCounter::::take()); weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)) } } /// Update `ContractInfo` with new fields that track storage deposits. mod v8 { use super::*; use sp_io::default_child_storage as child; use v6::ContractInfo as OldContractInfo; #[derive(Encode, Decode)] pub struct ContractInfo { pub trie_id: TrieId, pub code_hash: CodeHash, pub storage_bytes: u32, pub storage_items: u32, pub storage_byte_deposit: BalanceOf, pub storage_item_deposit: BalanceOf, pub storage_base_deposit: BalanceOf, } #[storage_alias] type ContractInfoOf = StorageMap, Twox64Concat, ::AccountId, V>; pub fn migrate(weight: &mut Weight) { >>::translate_values(|old: OldContractInfo| { // Count storage items of this contract let mut storage_bytes = 0u32; let mut storage_items = 0u32; let mut key = Vec::new(); while let Some(next) = child::next_key(&old.trie_id, &key) { key = next; let mut val_out = []; let len = child::read(&old.trie_id, &key, &mut val_out, 0) .expect("The loop conditions checks for existence of the key; qed"); storage_bytes.saturating_accrue(len); storage_items.saturating_accrue(1); } let storage_byte_deposit = T::DepositPerByte::get().saturating_mul(storage_bytes.into()); let storage_item_deposit = T::DepositPerItem::get().saturating_mul(storage_items.into()); let storage_base_deposit = old .storage_deposit .saturating_sub(storage_byte_deposit) .saturating_sub(storage_item_deposit); // Reads: One read for each storage item plus the contract info itself. // Writes: Only the new contract info. weight.saturating_accrue( T::DbWeight::get().reads_writes(u64::from(storage_items) + 1, 1), ); Some(ContractInfo { trie_id: old.trie_id, code_hash: old.code_hash, storage_bytes, storage_items, storage_byte_deposit, storage_item_deposit, storage_base_deposit, }) }); } #[cfg(feature = "try-runtime")] pub fn pre_upgrade() -> Result<(), &'static str> { use frame_support::traits::ReservableCurrency; for (key, value) in ContractInfoOf::>::iter() { let reserved = T::Currency::reserved_balance(&key); ensure!(reserved >= value.storage_deposit, "Reserved balance out of sync."); } Ok(()) } } /// Update `CodeStorage` with the new `determinism` field. mod v9 { use super::*; use crate::Determinism; use v6::PrefabWasmModule as OldPrefabWasmModule; #[derive(Encode, Decode)] pub struct PrefabWasmModule { #[codec(compact)] pub instruction_weights_version: u32, #[codec(compact)] pub initial: u32, #[codec(compact)] pub maximum: u32, pub code: Vec, pub determinism: Determinism, } #[storage_alias] type CodeStorage = StorageMap, Identity, CodeHash, PrefabWasmModule>; pub fn migrate(weight: &mut Weight) { >::translate_values(|old: OldPrefabWasmModule| { weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); Some(PrefabWasmModule { instruction_weights_version: old.instruction_weights_version, initial: old.initial, maximum: old.maximum, code: old.code, determinism: Determinism::Deterministic, }) }); } } // Post checks always need to be run against the latest storage version. This is why we // do not scope them in the per version modules. They always need to be ported to the latest // version. #[cfg(feature = "try-runtime")] mod post_checks { use super::*; use crate::Determinism; use sp_io::default_child_storage as child; use v8::ContractInfo; use v9::PrefabWasmModule; #[storage_alias] type CodeStorage = StorageMap, Identity, CodeHash, PrefabWasmModule>; #[storage_alias] type ContractInfoOf = StorageMap, Twox64Concat, ::AccountId, V>; pub fn post_upgrade(old_version: StorageVersion) -> Result<(), &'static str> { if old_version < 7 { return Ok(()) } if old_version < 8 { v8::()?; } if old_version < 9 { v9::()?; } Ok(()) } fn v8() -> Result<(), &'static str> { use frame_support::traits::ReservableCurrency; for (key, value) in ContractInfoOf::>::iter() { let reserved = T::Currency::reserved_balance(&key); let stored = value .storage_base_deposit .saturating_add(value.storage_byte_deposit) .saturating_add(value.storage_item_deposit); ensure!(reserved >= stored, "Reserved balance out of sync."); let mut storage_bytes = 0u32; let mut storage_items = 0u32; let mut key = Vec::new(); while let Some(next) = child::next_key(&value.trie_id, &key) { key = next; let mut val_out = []; let len = child::read(&value.trie_id, &key, &mut val_out, 0) .expect("The loop conditions checks for existence of the key; qed"); storage_bytes.saturating_accrue(len); storage_items.saturating_accrue(1); } ensure!(storage_bytes == value.storage_bytes, "Storage bytes do not match.",); ensure!(storage_items == value.storage_items, "Storage items do not match.",); } Ok(()) } fn v9() -> Result<(), &'static str> { for value in CodeStorage::::iter_values() { ensure!( value.determinism == Determinism::Deterministic, "All pre-existing codes need to be deterministic." ); } Ok(()) } }