mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 08:37:56 +00:00
447e753323
Co-authored-by: command-bot <>
272 lines
8.6 KiB
Rust
272 lines
8.6 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) 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.
|
|
|
|
//! Storage migrations for the preimage pallet.
|
|
|
|
use super::*;
|
|
use frame_support::{
|
|
storage_alias,
|
|
traits::{ConstU32, OnRuntimeUpgrade},
|
|
};
|
|
use sp_std::collections::btree_map::BTreeMap;
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
use frame_support::ensure;
|
|
#[cfg(feature = "try-runtime")]
|
|
use sp_runtime::TryRuntimeError;
|
|
|
|
/// The log target.
|
|
const TARGET: &'static str = "runtime::preimage::migration::v1";
|
|
|
|
/// The original data layout of the preimage pallet without a specific version number.
|
|
mod v0 {
|
|
use super::*;
|
|
|
|
#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)]
|
|
pub enum OldRequestStatus<AccountId, Balance> {
|
|
Unrequested(Option<(AccountId, Balance)>),
|
|
Requested(u32),
|
|
}
|
|
|
|
#[storage_alias]
|
|
pub type PreimageFor<T: Config> = StorageMap<
|
|
Pallet<T>,
|
|
Identity,
|
|
<T as frame_system::Config>::Hash,
|
|
BoundedVec<u8, ConstU32<MAX_SIZE>>,
|
|
>;
|
|
|
|
#[storage_alias]
|
|
pub type StatusFor<T: Config> = StorageMap<
|
|
Pallet<T>,
|
|
Identity,
|
|
<T as frame_system::Config>::Hash,
|
|
OldRequestStatus<<T as frame_system::Config>::AccountId, BalanceOf<T>>,
|
|
>;
|
|
|
|
/// Returns the number of images or `None` if the storage is corrupted.
|
|
#[cfg(feature = "try-runtime")]
|
|
pub fn image_count<T: Config>() -> Option<u32> {
|
|
let images = v0::PreimageFor::<T>::iter_values().count() as u32;
|
|
let status = v0::StatusFor::<T>::iter_values().count() as u32;
|
|
|
|
if images == status {
|
|
Some(images)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
pub mod v1 {
|
|
use super::*;
|
|
|
|
/// Migration for moving preimage from V0 to V1 storage.
|
|
///
|
|
/// Note: This needs to be run with the same hashing algorithm as before
|
|
/// since it is not re-hashing the preimages.
|
|
pub struct Migration<T>(sp_std::marker::PhantomData<T>);
|
|
|
|
impl<T: Config> OnRuntimeUpgrade for Migration<T> {
|
|
#[cfg(feature = "try-runtime")]
|
|
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
|
|
ensure!(StorageVersion::get::<Pallet<T>>() == 0, "can only upgrade from version 0");
|
|
|
|
let images = v0::image_count::<T>().expect("v0 storage corrupted");
|
|
log::info!(target: TARGET, "Migrating {} images", &images);
|
|
Ok((images as u32).encode())
|
|
}
|
|
|
|
fn on_runtime_upgrade() -> Weight {
|
|
let mut weight = T::DbWeight::get().reads(1);
|
|
if StorageVersion::get::<Pallet<T>>() != 0 {
|
|
log::warn!(
|
|
target: TARGET,
|
|
"skipping MovePreimagesIntoBuckets: executed on wrong storage version.\
|
|
Expected version 0"
|
|
);
|
|
return weight
|
|
}
|
|
|
|
let status = v0::StatusFor::<T>::drain().collect::<Vec<_>>();
|
|
weight.saturating_accrue(T::DbWeight::get().reads(status.len() as u64));
|
|
|
|
let preimages = v0::PreimageFor::<T>::drain().collect::<BTreeMap<_, _>>();
|
|
weight.saturating_accrue(T::DbWeight::get().reads(preimages.len() as u64));
|
|
|
|
for (hash, status) in status.into_iter() {
|
|
let preimage = if let Some(preimage) = preimages.get(&hash) {
|
|
preimage
|
|
} else {
|
|
log::error!(target: TARGET, "preimage not found for hash {:?}", &hash);
|
|
continue
|
|
};
|
|
let len = preimage.len() as u32;
|
|
if len > MAX_SIZE {
|
|
log::error!(
|
|
target: TARGET,
|
|
"preimage too large for hash {:?}, len: {}",
|
|
&hash,
|
|
len
|
|
);
|
|
continue
|
|
}
|
|
|
|
let status = match status {
|
|
v0::OldRequestStatus::Unrequested(deposit) => match deposit {
|
|
Some(deposit) => OldRequestStatus::Unrequested { deposit, len },
|
|
// `None` depositor becomes system-requested.
|
|
None =>
|
|
OldRequestStatus::Requested { deposit: None, count: 1, len: Some(len) },
|
|
},
|
|
v0::OldRequestStatus::Requested(0) => {
|
|
log::error!(target: TARGET, "preimage has counter of zero: {:?}", hash);
|
|
continue
|
|
},
|
|
v0::OldRequestStatus::Requested(count) =>
|
|
OldRequestStatus::Requested { deposit: None, count, len: Some(len) },
|
|
};
|
|
log::trace!(target: TARGET, "Moving preimage {:?} with len {}", hash, len);
|
|
|
|
#[allow(deprecated)]
|
|
crate::StatusFor::<T>::insert(hash, status);
|
|
crate::PreimageFor::<T>::insert(&(hash, len), preimage);
|
|
|
|
weight.saturating_accrue(T::DbWeight::get().writes(2));
|
|
}
|
|
StorageVersion::new(1).put::<Pallet<T>>();
|
|
|
|
weight.saturating_add(T::DbWeight::get().writes(1))
|
|
}
|
|
|
|
#[cfg(feature = "try-runtime")]
|
|
fn post_upgrade(state: Vec<u8>) -> DispatchResult {
|
|
let old_images: u32 =
|
|
Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed");
|
|
let new_images = image_count::<T>().expect("V1 storage corrupted");
|
|
|
|
if new_images != old_images {
|
|
log::error!(
|
|
target: TARGET,
|
|
"migrated {} images, expected {}",
|
|
new_images,
|
|
old_images
|
|
);
|
|
}
|
|
ensure!(StorageVersion::get::<Pallet<T>>() == 1, "must upgrade");
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Returns the number of images or `None` if the storage is corrupted.
|
|
#[cfg(feature = "try-runtime")]
|
|
pub fn image_count<T: Config>() -> Option<u32> {
|
|
// Use iter_values() to ensure that the values are decodable.
|
|
let images = crate::PreimageFor::<T>::iter_values().count() as u32;
|
|
#[allow(deprecated)]
|
|
let status = crate::StatusFor::<T>::iter_values().count() as u32;
|
|
|
|
if images == status {
|
|
Some(images)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[cfg(feature = "try-runtime")]
|
|
mod test {
|
|
#![allow(deprecated)]
|
|
use super::*;
|
|
use crate::mock::{Test as T, *};
|
|
|
|
use sp_runtime::bounded_vec;
|
|
|
|
#[test]
|
|
fn migration_works() {
|
|
new_test_ext().execute_with(|| {
|
|
assert_eq!(StorageVersion::get::<Pallet<T>>(), 0);
|
|
// Insert some preimages into the v0 storage:
|
|
|
|
// Case 1: Unrequested without deposit
|
|
let (p, h) = preimage::<T>(128);
|
|
v0::PreimageFor::<T>::insert(h, p);
|
|
v0::StatusFor::<T>::insert(h, v0::OldRequestStatus::Unrequested(None));
|
|
// Case 2: Unrequested with deposit
|
|
let (p, h) = preimage::<T>(1024);
|
|
v0::PreimageFor::<T>::insert(h, p);
|
|
v0::StatusFor::<T>::insert(h, v0::OldRequestStatus::Unrequested(Some((1, 1))));
|
|
// Case 3: Requested by 0 (invalid)
|
|
let (p, h) = preimage::<T>(8192);
|
|
v0::PreimageFor::<T>::insert(h, p);
|
|
v0::StatusFor::<T>::insert(h, v0::OldRequestStatus::Requested(0));
|
|
// Case 4: Requested by 10
|
|
let (p, h) = preimage::<T>(65536);
|
|
v0::PreimageFor::<T>::insert(h, p);
|
|
v0::StatusFor::<T>::insert(h, v0::OldRequestStatus::Requested(10));
|
|
|
|
assert_eq!(v0::image_count::<T>(), Some(4));
|
|
assert_eq!(v1::image_count::<T>(), None, "V1 storage should be corrupted");
|
|
|
|
let state = v1::Migration::<T>::pre_upgrade().unwrap();
|
|
let _w = v1::Migration::<T>::on_runtime_upgrade();
|
|
v1::Migration::<T>::post_upgrade(state).unwrap();
|
|
|
|
// V0 and V1 share the same prefix, so `iter_values` still counts the same.
|
|
assert_eq!(v0::image_count::<T>(), Some(3));
|
|
assert_eq!(v1::image_count::<T>(), Some(3)); // One gets skipped therefore 3.
|
|
assert_eq!(StorageVersion::get::<Pallet<T>>(), 1);
|
|
|
|
// Case 1: Unrequested without deposit becomes system-requested
|
|
let (p, h) = preimage::<T>(128);
|
|
assert_eq!(crate::PreimageFor::<T>::get(&(h, 128)), Some(p));
|
|
assert_eq!(
|
|
crate::StatusFor::<T>::get(h),
|
|
Some(OldRequestStatus::Requested { deposit: None, count: 1, len: Some(128) })
|
|
);
|
|
// Case 2: Unrequested with deposit becomes unrequested
|
|
let (p, h) = preimage::<T>(1024);
|
|
assert_eq!(crate::PreimageFor::<T>::get(&(h, 1024)), Some(p));
|
|
assert_eq!(
|
|
crate::StatusFor::<T>::get(h),
|
|
Some(OldRequestStatus::Unrequested { deposit: (1, 1), len: 1024 })
|
|
);
|
|
// Case 3: Requested by 0 should be skipped
|
|
let (_, h) = preimage::<T>(8192);
|
|
assert_eq!(crate::PreimageFor::<T>::get(&(h, 8192)), None);
|
|
assert_eq!(crate::StatusFor::<T>::get(h), None);
|
|
// Case 4: Requested by 10 becomes requested by 10
|
|
let (p, h) = preimage::<T>(65536);
|
|
assert_eq!(crate::PreimageFor::<T>::get(&(h, 65536)), Some(p));
|
|
assert_eq!(
|
|
crate::StatusFor::<T>::get(h),
|
|
Some(OldRequestStatus::Requested { deposit: None, count: 10, len: Some(65536) })
|
|
);
|
|
});
|
|
}
|
|
|
|
/// Returns a preimage with a given size and its hash.
|
|
fn preimage<T: Config>(
|
|
len: usize,
|
|
) -> (BoundedVec<u8, ConstU32<MAX_SIZE>>, <T as frame_system::Config>::Hash) {
|
|
let p = bounded_vec![1; len];
|
|
let h = <T as frame_system::Config>::Hashing::hash_of(&p);
|
|
(p, h)
|
|
}
|
|
}
|