BREAKING - Try-runtime: Use proper error types (#13993)

* Try-state: DispatchResult as return type

* try_state for the rest of the pallets

* pre_upgrade

* post_upgrade

* try_runtime_upgrade

* fixes

* bags-list fix

* fix

* update test

* warning fix

* ...

* final fixes 🤞

* warning..

* frame-support

* warnings

* Update frame/staking/src/migrations.rs

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>

* fix

* fix warning

* nit fix

* merge fixes

* small fix

* should be good now

* missed these ones

* introduce TryRuntimeError and TryRuntimeResult

* fixes

* fix

* removed TryRuntimeResult & made some fixes

* fix testsg

* tests passing

* unnecessary imports

* Update frame/assets/src/migration.rs

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>

---------

Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>
Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>
This commit is contained in:
Sergej Sakac
2023-05-23 08:56:10 +02:00
committed by GitHub
parent 918d1ef80d
commit df87bae1a9
34 changed files with 419 additions and 276 deletions
+15 -11
View File
@@ -18,6 +18,9 @@
use super::*; use super::*;
use frame_support::{log, traits::OnRuntimeUpgrade}; use frame_support::{log, traits::OnRuntimeUpgrade};
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
pub mod v1 { pub mod v1 {
use frame_support::{pallet_prelude::*, weights::Weight}; use frame_support::{pallet_prelude::*, weights::Weight};
@@ -92,7 +95,7 @@ pub mod v1 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
frame_support::ensure!( frame_support::ensure!(
Pallet::<T>::on_chain_storage_version() == 0, Pallet::<T>::on_chain_storage_version() == 0,
"must upgrade linearly" "must upgrade linearly"
@@ -102,13 +105,13 @@ pub mod v1 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(prev_count: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(prev_count: Vec<u8>) -> Result<(), TryRuntimeError> {
let prev_count: u32 = Decode::decode(&mut prev_count.as_slice()).expect( let prev_count: u32 = Decode::decode(&mut prev_count.as_slice()).expect(
"the state parameter should be something that was generated by pre_upgrade", "the state parameter should be something that was generated by pre_upgrade",
); );
let post_count = Asset::<T>::iter().count() as u32; let post_count = Asset::<T>::iter().count() as u32;
assert_eq!( ensure!(
prev_count, post_count, prev_count == post_count,
"the asset count before and after the migration should be the same" "the asset count before and after the migration should be the same"
); );
@@ -116,17 +119,18 @@ pub mod v1 {
let onchain_version = Pallet::<T>::on_chain_storage_version(); let onchain_version = Pallet::<T>::on_chain_storage_version();
frame_support::ensure!(current_version == 1, "must_upgrade"); frame_support::ensure!(current_version == 1, "must_upgrade");
assert_eq!( ensure!(
current_version, onchain_version, current_version == onchain_version,
"after migration, the current_version and onchain_version should be the same" "after migration, the current_version and onchain_version should be the same"
); );
Asset::<T>::iter().for_each(|(_id, asset)| { Asset::<T>::iter().try_for_each(|(_id, asset)| -> Result<(), TryRuntimeError> {
assert!( ensure!(
asset.status == AssetStatus::Live || asset.status == AssetStatus::Frozen, asset.status == AssetStatus::Live || asset.status == AssetStatus::Frozen,
"assets should only be live or frozen. None should be in destroying status, or undefined state" "assets should only be live or frozen. None should be in destroying status, or undefined state"
) );
}); Ok(())
})?;
Ok(()) Ok(())
} }
} }
+6 -3
View File
@@ -59,6 +59,9 @@ use frame_system::ensure_signed;
use sp_runtime::traits::{AtLeast32BitUnsigned, Bounded, StaticLookup}; use sp_runtime::traits::{AtLeast32BitUnsigned, Bounded, StaticLookup};
use sp_std::prelude::*; use sp_std::prelude::*;
#[cfg(any(test, feature = "try-runtime", feature = "fuzz"))]
use sp_runtime::TryRuntimeError;
#[cfg(any(feature = "runtime-benchmarks", test))] #[cfg(any(feature = "runtime-benchmarks", test))]
mod benchmarks; mod benchmarks;
@@ -267,7 +270,7 @@ pub mod pallet {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn try_state(_: BlockNumberFor<T>) -> Result<(), &'static str> { fn try_state(_: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
<Self as SortedListProvider<T::AccountId>>::try_state() <Self as SortedListProvider<T::AccountId>>::try_state()
} }
} }
@@ -275,7 +278,7 @@ pub mod pallet {
#[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] #[cfg(any(test, feature = "try-runtime", feature = "fuzz"))]
impl<T: Config<I>, I: 'static> Pallet<T, I> { impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub fn do_try_state() -> Result<(), &'static str> { pub fn do_try_state() -> Result<(), TryRuntimeError> {
List::<T, I>::do_try_state() List::<T, I>::do_try_state()
} }
} }
@@ -355,7 +358,7 @@ impl<T: Config<I>, I: 'static> SortedListProvider<T::AccountId> for Pallet<T, I>
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn try_state() -> Result<(), &'static str> { fn try_state() -> Result<(), TryRuntimeError> {
Self::do_try_state() Self::do_try_state()
} }
+8 -8
View File
@@ -42,6 +42,9 @@ use sp_std::{
prelude::*, prelude::*,
}; };
#[cfg(any(test, feature = "try-runtime", feature = "fuzz"))]
use sp_runtime::TryRuntimeError;
#[derive(Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo, PalletError)] #[derive(Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo, PalletError)]
pub enum ListError { pub enum ListError {
/// A duplicate id has been detected. /// A duplicate id has been detected.
@@ -512,11 +515,11 @@ impl<T: Config<I>, I: 'static> List<T, I> {
/// * and sanity-checks all bags and nodes. This will cascade down all the checks and makes sure /// * and sanity-checks all bags and nodes. This will cascade down all the checks and makes sure
/// all bags and nodes are checked per *any* update to `List`. /// all bags and nodes are checked per *any* update to `List`.
#[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] #[cfg(any(test, feature = "try-runtime", feature = "fuzz"))]
pub(crate) fn do_try_state() -> Result<(), &'static str> { pub(crate) fn do_try_state() -> Result<(), TryRuntimeError> {
let mut seen_in_list = BTreeSet::new(); let mut seen_in_list = BTreeSet::new();
ensure!( ensure!(
Self::iter().map(|node| node.id).all(|id| seen_in_list.insert(id)), Self::iter().map(|node| node.id).all(|id| seen_in_list.insert(id)),
"duplicate identified", "duplicate identified"
); );
let iter_count = Self::iter().count() as u32; let iter_count = Self::iter().count() as u32;
@@ -750,7 +753,7 @@ impl<T: Config<I>, I: 'static> Bag<T, I> {
/// * Ensures tail has no next. /// * Ensures tail has no next.
/// * Ensures there are no loops, traversal from head to tail is correct. /// * Ensures there are no loops, traversal from head to tail is correct.
#[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] #[cfg(any(test, feature = "try-runtime", feature = "fuzz"))]
fn do_try_state(&self) -> Result<(), &'static str> { fn do_try_state(&self) -> Result<(), TryRuntimeError> {
frame_support::ensure!( frame_support::ensure!(
self.head() self.head()
.map(|head| head.prev().is_none()) .map(|head| head.prev().is_none())
@@ -895,15 +898,12 @@ impl<T: Config<I>, I: 'static> Node<T, I> {
} }
#[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] #[cfg(any(test, feature = "try-runtime", feature = "fuzz"))]
fn do_try_state(&self) -> Result<(), &'static str> { fn do_try_state(&self) -> Result<(), TryRuntimeError> {
let expected_bag = Bag::<T, I>::get(self.bag_upper).ok_or("bag not found for node")?; let expected_bag = Bag::<T, I>::get(self.bag_upper).ok_or("bag not found for node")?;
let id = self.id(); let id = self.id();
frame_support::ensure!( frame_support::ensure!(expected_bag.contains(id), "node does not exist in the bag");
expected_bag.contains(id),
"node does not exist in the expected bag"
);
let non_terminal_check = !self.is_terminal() && let non_terminal_check = !self.is_terminal() &&
expected_bag.head.as_ref() != Some(id) && expected_bag.head.as_ref() != Some(id) &&
+9 -2
View File
@@ -22,6 +22,7 @@ use crate::{
}; };
use frame_election_provider_support::{SortedListProvider, VoteWeight}; use frame_election_provider_support::{SortedListProvider, VoteWeight};
use frame_support::{assert_ok, assert_storage_noop}; use frame_support::{assert_ok, assert_storage_noop};
use sp_runtime::TryRuntimeError;
fn node( fn node(
id: AccountId, id: AccountId,
@@ -359,7 +360,10 @@ mod list {
// make sure there are no duplicates. // make sure there are no duplicates.
ExtBuilder::default().build_and_execute_no_post_check(|| { ExtBuilder::default().build_and_execute_no_post_check(|| {
Bag::<Runtime>::get(10).unwrap().insert_unchecked(2, 10); Bag::<Runtime>::get(10).unwrap().insert_unchecked(2, 10);
assert_eq!(List::<Runtime>::do_try_state(), Err("duplicate identified")); assert_eq!(
List::<Runtime>::do_try_state(),
TryRuntimeError::Other("duplicate identified").into()
);
}); });
// ensure count is in sync with `ListNodes::count()`. // ensure count is in sync with `ListNodes::count()`.
@@ -373,7 +377,10 @@ mod list {
CounterForListNodes::<Runtime>::mutate(|counter| *counter += 1); CounterForListNodes::<Runtime>::mutate(|counter| *counter += 1);
assert_eq!(crate::ListNodes::<Runtime>::count(), 5); assert_eq!(crate::ListNodes::<Runtime>::count(), 5);
assert_eq!(List::<Runtime>::do_try_state(), Err("iter_count != stored_count")); assert_eq!(
List::<Runtime>::do_try_state(),
TryRuntimeError::Other("iter_count != stored_count").into()
);
}); });
} }
+6 -3
View File
@@ -24,6 +24,9 @@ use frame_support::traits::OnRuntimeUpgrade;
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
use frame_support::ensure; use frame_support::ensure;
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
use sp_std::vec::Vec; use sp_std::vec::Vec;
@@ -35,7 +38,7 @@ impl<T: crate::Config<I>, I: 'static> OnRuntimeUpgrade for CheckCounterPrefix<T,
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
// The old explicit storage item. // The old explicit storage item.
#[frame_support::storage_alias] #[frame_support::storage_alias]
type CounterForListNodes<T: crate::Config<I>, I: 'static> = type CounterForListNodes<T: crate::Config<I>, I: 'static> =
@@ -88,7 +91,7 @@ mod old {
pub struct AddScore<T: crate::Config<I>, I: 'static = ()>(sp_std::marker::PhantomData<(T, I)>); pub struct AddScore<T: crate::Config<I>, I: 'static = ()>(sp_std::marker::PhantomData<(T, I)>);
impl<T: crate::Config<I>, I: 'static> OnRuntimeUpgrade for AddScore<T, I> { impl<T: crate::Config<I>, I: 'static> OnRuntimeUpgrade for AddScore<T, I> {
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
// The list node data should be corrupt at this point, so this is zero. // The list node data should be corrupt at this point, so this is zero.
ensure!(crate::ListNodes::<T, I>::iter().count() == 0, "list node data is not corrupt"); ensure!(crate::ListNodes::<T, I>::iter().count() == 0, "list node data is not corrupt");
// We can use the helper `old::ListNode` to get the existing data. // We can use the helper `old::ListNode` to get the existing data.
@@ -119,7 +122,7 @@ impl<T: crate::Config<I>, I: 'static> OnRuntimeUpgrade for AddScore<T, I> {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(node_count_before: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(node_count_before: Vec<u8>) -> Result<(), TryRuntimeError> {
let node_count_before: u32 = Decode::decode(&mut node_count_before.as_slice()) let node_count_before: u32 = Decode::decode(&mut node_count_before.as_slice())
.expect("the state parameter should be something that was generated by pre_upgrade"); .expect("the state parameter should be something that was generated by pre_upgrade");
// Now the list node data is not corrupt anymore. // Now the list node data is not corrupt anymore.
+53 -50
View File
@@ -60,6 +60,9 @@ use frame_support::{
weights::Weight, weights::Weight,
}; };
#[cfg(any(feature = "try-runtime", test))]
use sp_runtime::TryRuntimeError;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@@ -346,9 +349,8 @@ pub mod pallet {
#[pallet::hooks] #[pallet::hooks]
impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> { impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn try_state(_n: BlockNumberFor<T>) -> Result<(), &'static str> { fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
Self::do_try_state()?; Self::do_try_state()
Ok(())
} }
} }
@@ -967,77 +969,78 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Looking at prime account: /// Looking at prime account:
/// * The prime account must be a member of the collective. /// * The prime account must be a member of the collective.
#[cfg(any(feature = "try-runtime", test))] #[cfg(any(feature = "try-runtime", test))]
fn do_try_state() -> DispatchResult { fn do_try_state() -> Result<(), TryRuntimeError> {
Self::proposals().into_iter().try_for_each(|proposal| -> DispatchResult { Self::proposals()
ensure!( .into_iter()
Self::proposal_of(proposal).is_some(), .try_for_each(|proposal| -> Result<(), TryRuntimeError> {
DispatchError::Other( ensure!(
Self::proposal_of(proposal).is_some(),
"Proposal hash from `Proposals` is not found inside the `ProposalOf` mapping." "Proposal hash from `Proposals` is not found inside the `ProposalOf` mapping."
) );
); Ok(())
Ok(()) })?;
})?;
ensure!( ensure!(
Self::proposals().into_iter().count() <= Self::proposal_count() as usize, Self::proposals().into_iter().count() <= Self::proposal_count() as usize,
DispatchError::Other("The actual number of proposals is greater than `ProposalCount`") "The actual number of proposals is greater than `ProposalCount`"
); );
ensure!( ensure!(
Self::proposals().into_iter().count() == <ProposalOf<T, I>>::iter_keys().count(), Self::proposals().into_iter().count() == <ProposalOf<T, I>>::iter_keys().count(),
DispatchError::Other("Proposal count inside `Proposals` is not equal to the proposal count in `ProposalOf`") "Proposal count inside `Proposals` is not equal to the proposal count in `ProposalOf`"
); );
Self::proposals().into_iter().try_for_each(|proposal| -> DispatchResult { Self::proposals()
if let Some(votes) = Self::voting(proposal) { .into_iter()
let ayes = votes.ayes.len(); .try_for_each(|proposal| -> Result<(), TryRuntimeError> {
let nays = votes.nays.len(); if let Some(votes) = Self::voting(proposal) {
let ayes = votes.ayes.len();
let nays = votes.nays.len();
ensure!( ensure!(
ayes.saturating_add(nays) <= T::MaxMembers::get() as usize, ayes.saturating_add(nays) <= T::MaxMembers::get() as usize,
DispatchError::Other("The sum of ayes and nays is greater than `MaxMembers`") "The sum of ayes and nays is greater than `MaxMembers`"
); );
} }
Ok(()) Ok(())
})?; })?;
let mut proposal_indices = vec![]; let mut proposal_indices = vec![];
Self::proposals().into_iter().try_for_each(|proposal| -> DispatchResult { Self::proposals()
if let Some(votes) = Self::voting(proposal) { .into_iter()
let proposal_index = votes.index; .try_for_each(|proposal| -> Result<(), TryRuntimeError> {
ensure!( if let Some(votes) = Self::voting(proposal) {
!proposal_indices.contains(&proposal_index), let proposal_index = votes.index;
DispatchError::Other("The proposal index is not unique.") ensure!(
); !proposal_indices.contains(&proposal_index),
proposal_indices.push(proposal_index); "The proposal index is not unique."
} );
Ok(()) proposal_indices.push(proposal_index);
})?; }
Ok(())
})?;
<Voting<T, I>>::iter_keys().try_for_each(|proposal_hash| -> DispatchResult { <Voting<T, I>>::iter_keys().try_for_each(
ensure!( |proposal_hash| -> Result<(), TryRuntimeError> {
Self::proposals().contains(&proposal_hash), ensure!(
DispatchError::Other( Self::proposals().contains(&proposal_hash),
"`Proposals` doesn't contain the proposal hash from the `Voting` storage map." "`Proposals` doesn't contain the proposal hash from the `Voting` storage map."
) );
); Ok(())
Ok(()) },
})?; )?;
ensure!( ensure!(
Self::members().len() <= T::MaxMembers::get() as usize, Self::members().len() <= T::MaxMembers::get() as usize,
DispatchError::Other("The member count is greater than `MaxMembers`.") "The member count is greater than `MaxMembers`."
); );
ensure!( ensure!(
Self::members().windows(2).all(|members| members[0] <= members[1]), Self::members().windows(2).all(|members| members[0] <= members[1]),
DispatchError::Other("The members are not sorted by value.") "The members are not sorted by value."
); );
if let Some(prime) = Self::prime() { if let Some(prime) = Self::prime() {
ensure!( ensure!(Self::members().contains(&prime), "Prime account is not a member.");
Self::members().contains(&prime),
DispatchError::Other("Prime account is not a member.")
);
} }
Ok(()) Ok(())
+11 -8
View File
@@ -28,6 +28,9 @@ use frame_support::{
use sp_runtime::traits::Saturating; use sp_runtime::traits::Saturating;
use sp_std::{marker::PhantomData, prelude::*}; use sp_std::{marker::PhantomData, prelude::*};
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
/// Performs all necessary migrations based on `StorageVersion`. /// Performs all necessary migrations based on `StorageVersion`.
pub struct Migration<T: Config>(PhantomData<T>); pub struct Migration<T: Config>(PhantomData<T>);
impl<T: Config> OnRuntimeUpgrade for Migration<T> { impl<T: Config> OnRuntimeUpgrade for Migration<T> {
@@ -66,7 +69,7 @@ impl<T: Config> OnRuntimeUpgrade for Migration<T> {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
let version = <Pallet<T>>::on_chain_storage_version(); let version = <Pallet<T>>::on_chain_storage_version();
if version == 7 { if version == 7 {
@@ -77,7 +80,7 @@ impl<T: Config> OnRuntimeUpgrade for Migration<T> {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(state: Vec<u8>) -> Result<(), TryRuntimeError> {
let version = Decode::decode(&mut state.as_ref()).map_err(|_| "Cannot decode version")?; let version = Decode::decode(&mut state.as_ref()).map_err(|_| "Cannot decode version")?;
post_checks::post_upgrade::<T>(version) post_checks::post_upgrade::<T>(version)
} }
@@ -355,7 +358,7 @@ mod v8 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
pub fn pre_upgrade<T: Config>() -> Result<(), &'static str> { pub fn pre_upgrade<T: Config>() -> Result<(), TryRuntimeError> {
use frame_support::traits::ReservableCurrency; use frame_support::traits::ReservableCurrency;
for (key, value) in ContractInfoOf::<T, OldContractInfo<T>>::iter() { for (key, value) in ContractInfoOf::<T, OldContractInfo<T>>::iter() {
let reserved = T::Currency::reserved_balance(&key); let reserved = T::Currency::reserved_balance(&key);
@@ -418,7 +421,7 @@ mod post_checks {
type ContractInfoOf<T: Config, V> = type ContractInfoOf<T: Config, V> =
StorageMap<Pallet<T>, Twox64Concat, <T as frame_system::Config>::AccountId, V>; StorageMap<Pallet<T>, Twox64Concat, <T as frame_system::Config>::AccountId, V>;
pub fn post_upgrade<T: Config>(old_version: StorageVersion) -> Result<(), &'static str> { pub fn post_upgrade<T: Config>(old_version: StorageVersion) -> Result<(), TryRuntimeError> {
if old_version < 7 { if old_version < 7 {
return Ok(()) return Ok(())
} }
@@ -434,7 +437,7 @@ mod post_checks {
Ok(()) Ok(())
} }
fn v8<T: Config>() -> Result<(), &'static str> { fn v8<T: Config>() -> Result<(), TryRuntimeError> {
use frame_support::traits::ReservableCurrency; use frame_support::traits::ReservableCurrency;
for (key, value) in ContractInfoOf::<T, ContractInfo<T>>::iter() { for (key, value) in ContractInfoOf::<T, ContractInfo<T>>::iter() {
let reserved = T::Currency::reserved_balance(&key); let reserved = T::Currency::reserved_balance(&key);
@@ -455,13 +458,13 @@ mod post_checks {
storage_bytes.saturating_accrue(len); storage_bytes.saturating_accrue(len);
storage_items.saturating_accrue(1); storage_items.saturating_accrue(1);
} }
ensure!(storage_bytes == value.storage_bytes, "Storage bytes do not match.",); ensure!(storage_bytes == value.storage_bytes, "Storage bytes do not match.");
ensure!(storage_items == value.storage_items, "Storage items do not match.",); ensure!(storage_items == value.storage_items, "Storage items do not match.");
} }
Ok(()) Ok(())
} }
fn v9<T: Config>() -> Result<(), &'static str> { fn v9<T: Config>() -> Result<(), TryRuntimeError> {
for value in CodeStorage::<T>::iter_values() { for value in CodeStorage::<T>::iter_values() {
ensure!( ensure!(
value.determinism == Determinism::Enforced, value.determinism == Determinism::Enforced,
+7 -7
View File
@@ -61,12 +61,12 @@ pub mod v1 {
impl<T: Config + frame_system::Config<Hash = H256>> OnRuntimeUpgrade for Migration<T> { impl<T: Config + frame_system::Config<Hash = H256>> OnRuntimeUpgrade for Migration<T> {
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
assert_eq!(StorageVersion::get::<Pallet<T>>(), 0, "can only upgrade from version 0"); ensure!(StorageVersion::get::<Pallet<T>>() == 0, "can only upgrade from version 0");
let props_count = v0::PublicProps::<T>::get().len(); let props_count = v0::PublicProps::<T>::get().len();
log::info!(target: TARGET, "{} public proposals will be migrated.", props_count,); log::info!(target: TARGET, "{} public proposals will be migrated.", props_count,);
ensure!(props_count <= T::MaxProposals::get() as usize, "too many proposals"); ensure!(props_count <= T::MaxProposals::get() as usize, Error::<T>::TooMany);
let referenda_count = v0::ReferendumInfoOf::<T>::iter().count(); let referenda_count = v0::ReferendumInfoOf::<T>::iter().count();
log::info!(target: TARGET, "{} referenda will be migrated.", referenda_count); log::info!(target: TARGET, "{} referenda will be migrated.", referenda_count);
@@ -133,15 +133,15 @@ pub mod v1 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
assert_eq!(StorageVersion::get::<Pallet<T>>(), 1, "must upgrade"); ensure!(StorageVersion::get::<Pallet<T>>() == 1, "must upgrade");
let (old_props_count, old_ref_count): (u32, u32) = let (old_props_count, old_ref_count): (u32, u32) =
Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed"); Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed");
let new_props_count = crate::PublicProps::<T>::get().len() as u32; let new_props_count = crate::PublicProps::<T>::get().len() as u32;
assert_eq!(new_props_count, old_props_count, "must migrate all public proposals"); ensure!(new_props_count == old_props_count, "must migrate all public proposals");
let new_ref_count = crate::ReferendumInfoOf::<T>::iter().count() as u32; let new_ref_count = crate::ReferendumInfoOf::<T>::iter().count() as u32;
assert_eq!(new_ref_count, old_ref_count, "must migrate all referenda"); ensure!(new_ref_count == old_ref_count, "must migrate all referenda");
log::info!( log::info!(
target: TARGET, target: TARGET,
@@ -257,6 +257,9 @@ use sp_runtime::{
}; };
use sp_std::prelude::*; use sp_std::prelude::*;
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
#[cfg(feature = "runtime-benchmarks")] #[cfg(feature = "runtime-benchmarks")]
mod benchmarking; mod benchmarking;
#[cfg(test)] #[cfg(test)]
@@ -883,7 +886,7 @@ pub mod pallet {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn try_state(_n: T::BlockNumber) -> Result<(), &'static str> { fn try_state(_n: T::BlockNumber) -> Result<(), TryRuntimeError> {
Self::do_try_state() Self::do_try_state()
} }
} }
@@ -1579,7 +1582,7 @@ impl<T: Config> Pallet<T> {
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
impl<T: Config> Pallet<T> { impl<T: Config> Pallet<T> {
fn do_try_state() -> Result<(), &'static str> { fn do_try_state() -> Result<(), TryRuntimeError> {
Self::try_state_snapshot()?; Self::try_state_snapshot()?;
Self::try_state_signed_submissions_map()?; Self::try_state_signed_submissions_map()?;
Self::try_state_phase_off() Self::try_state_phase_off()
@@ -1588,7 +1591,7 @@ impl<T: Config> Pallet<T> {
// [`Snapshot`] state check. Invariants: // [`Snapshot`] state check. Invariants:
// - [`DesiredTargets`] exists if and only if [`Snapshot`] is present. // - [`DesiredTargets`] exists if and only if [`Snapshot`] is present.
// - [`SnapshotMetadata`] exist if and only if [`Snapshot`] is present. // - [`SnapshotMetadata`] exist if and only if [`Snapshot`] is present.
fn try_state_snapshot() -> Result<(), &'static str> { fn try_state_snapshot() -> Result<(), TryRuntimeError> {
if <Snapshot<T>>::exists() && if <Snapshot<T>>::exists() &&
<SnapshotMetadata<T>>::exists() && <SnapshotMetadata<T>>::exists() &&
<DesiredTargets<T>>::exists() <DesiredTargets<T>>::exists()
@@ -1600,7 +1603,7 @@ impl<T: Config> Pallet<T> {
{ {
Ok(()) Ok(())
} else { } else {
Err("If snapshot exists, metadata and desired targets should be set too. Otherwise, none should be set.") Err("If snapshot exists, metadata and desired targets should be set too. Otherwise, none should be set.".into())
} }
} }
@@ -1608,28 +1611,34 @@ impl<T: Config> Pallet<T> {
// - All [`SignedSubmissionIndices`] are present in [`SignedSubmissionsMap`], and no more; // - All [`SignedSubmissionIndices`] are present in [`SignedSubmissionsMap`], and no more;
// - [`SignedSubmissionNextIndex`] is not present in [`SignedSubmissionsMap`]; // - [`SignedSubmissionNextIndex`] is not present in [`SignedSubmissionsMap`];
// - [`SignedSubmissionIndices`] is sorted by election score. // - [`SignedSubmissionIndices`] is sorted by election score.
fn try_state_signed_submissions_map() -> Result<(), &'static str> { fn try_state_signed_submissions_map() -> Result<(), TryRuntimeError> {
let mut last_score: ElectionScore = Default::default(); let mut last_score: ElectionScore = Default::default();
let indices = <SignedSubmissionIndices<T>>::get(); let indices = <SignedSubmissionIndices<T>>::get();
for (i, indice) in indices.iter().enumerate() { for (i, indice) in indices.iter().enumerate() {
let submission = <SignedSubmissionsMap<T>>::get(indice.2); let submission = <SignedSubmissionsMap<T>>::get(indice.2);
if submission.is_none() { if submission.is_none() {
return Err("All signed submissions indices must be part of the submissions map") return Err(
"All signed submissions indices must be part of the submissions map".into()
)
} }
if i == 0 { if i == 0 {
last_score = indice.0 last_score = indice.0
} else { } else {
if last_score.strict_threshold_better(indice.0, Perbill::zero()) { if last_score.strict_threshold_better(indice.0, Perbill::zero()) {
return Err("Signed submission indices vector must be ordered by election score") return Err(
"Signed submission indices vector must be ordered by election score".into()
)
} }
last_score = indice.0; last_score = indice.0;
} }
} }
if <SignedSubmissionsMap<T>>::iter().nth(indices.len()).is_some() { if <SignedSubmissionsMap<T>>::iter().nth(indices.len()).is_some() {
return Err("Signed submissions map length should be the same as the indices vec length") return Err(
"Signed submissions map length should be the same as the indices vec length".into()
)
} }
match <SignedSubmissionNextIndex<T>>::get() { match <SignedSubmissionNextIndex<T>>::get() {
@@ -1637,7 +1646,8 @@ impl<T: Config> Pallet<T> {
next => next =>
if <SignedSubmissionsMap<T>>::get(next).is_some() { if <SignedSubmissionsMap<T>>::get(next).is_some() {
return Err( return Err(
"The next submissions index should not be in the submissions maps already", "The next submissions index should not be in the submissions maps already"
.into(),
) )
} else { } else {
Ok(()) Ok(())
@@ -1647,12 +1657,12 @@ impl<T: Config> Pallet<T> {
// [`Phase::Off`] state check. Invariants: // [`Phase::Off`] state check. Invariants:
// - If phase is `Phase::Off`, [`Snapshot`] must be none. // - If phase is `Phase::Off`, [`Snapshot`] must be none.
fn try_state_phase_off() -> Result<(), &'static str> { fn try_state_phase_off() -> Result<(), TryRuntimeError> {
match Self::current_phase().is_off() { match Self::current_phase().is_off() {
false => Ok(()), false => Ok(()),
true => true =>
if <Snapshot<T>>::get().is_some() { if <Snapshot<T>>::get().is_some() {
Err("Snapshot must be none when in Phase::Off") Err("Snapshot must be none when in Phase::Off".into())
} else { } else {
Ok(()) Ok(())
}, },
@@ -189,6 +189,9 @@ pub use sp_npos_elections::{
}; };
pub use traits::NposSolution; pub use traits::NposSolution;
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
// re-export for the solution macro, with the dependencies of the macro. // re-export for the solution macro, with the dependencies of the macro.
#[doc(hidden)] #[doc(hidden)]
pub use codec; pub use codec;
@@ -564,7 +567,7 @@ pub trait SortedListProvider<AccountId> {
/// Check internal state of the list. Only meant for debugging. /// Check internal state of the list. Only meant for debugging.
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn try_state() -> Result<(), &'static str>; fn try_state() -> Result<(), TryRuntimeError>;
/// If `who` changes by the returned amount they are guaranteed to have a worst case change /// If `who` changes by the returned amount they are guaranteed to have a worst case change
/// in their list position. /// in their list position.
+19 -14
View File
@@ -115,6 +115,9 @@ use sp_runtime::{
}; };
use sp_std::{cmp::Ordering, prelude::*}; use sp_std::{cmp::Ordering, prelude::*};
#[cfg(any(feature = "try-runtime", test))]
use sp_runtime::TryRuntimeError;
mod benchmarking; mod benchmarking;
pub mod weights; pub mod weights;
pub use weights::WeightInfo; pub use weights::WeightInfo;
@@ -327,7 +330,7 @@ pub mod pallet {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn try_state(_n: T::BlockNumber) -> Result<(), &'static str> { fn try_state(_n: T::BlockNumber) -> Result<(), TryRuntimeError> {
Self::do_try_state() Self::do_try_state()
} }
} }
@@ -1193,7 +1196,7 @@ impl<T: Config> ContainsLengthBound for Pallet<T> {
#[cfg(any(feature = "try-runtime", test))] #[cfg(any(feature = "try-runtime", test))]
impl<T: Config> Pallet<T> { impl<T: Config> Pallet<T> {
fn do_try_state() -> Result<(), &'static str> { fn do_try_state() -> Result<(), TryRuntimeError> {
Self::try_state_members()?; Self::try_state_members()?;
Self::try_state_runners_up()?; Self::try_state_runners_up()?;
Self::try_state_candidates()?; Self::try_state_candidates()?;
@@ -1204,20 +1207,20 @@ impl<T: Config> Pallet<T> {
/// [`Members`] state checks. Invariants: /// [`Members`] state checks. Invariants:
/// - Members are always sorted based on account ID. /// - Members are always sorted based on account ID.
fn try_state_members() -> Result<(), &'static str> { fn try_state_members() -> Result<(), TryRuntimeError> {
let mut members = Members::<T>::get().clone(); let mut members = Members::<T>::get().clone();
members.sort_by_key(|m| m.who.clone()); members.sort_by_key(|m| m.who.clone());
if Members::<T>::get() == members { if Members::<T>::get() == members {
Ok(()) Ok(())
} else { } else {
Err("try_state checks: Members must be always sorted by account ID") Err("try_state checks: Members must be always sorted by account ID".into())
} }
} }
// [`RunnersUp`] state checks. Invariants: // [`RunnersUp`] state checks. Invariants:
// - Elements are sorted based on weight (worst to best). // - Elements are sorted based on weight (worst to best).
fn try_state_runners_up() -> Result<(), &'static str> { fn try_state_runners_up() -> Result<(), TryRuntimeError> {
let mut sorted = RunnersUp::<T>::get(); let mut sorted = RunnersUp::<T>::get();
// worst stake first // worst stake first
sorted.sort_by(|a, b| a.stake.cmp(&b.stake)); sorted.sort_by(|a, b| a.stake.cmp(&b.stake));
@@ -1225,27 +1228,28 @@ impl<T: Config> Pallet<T> {
if RunnersUp::<T>::get() == sorted { if RunnersUp::<T>::get() == sorted {
Ok(()) Ok(())
} else { } else {
Err("try_state checks: Runners Up must always be sorted by stake (worst to best)") Err("try_state checks: Runners Up must always be sorted by stake (worst to best)"
.into())
} }
} }
// [`Candidates`] state checks. Invariants: // [`Candidates`] state checks. Invariants:
// - Always sorted based on account ID. // - Always sorted based on account ID.
fn try_state_candidates() -> Result<(), &'static str> { fn try_state_candidates() -> Result<(), TryRuntimeError> {
let mut candidates = Candidates::<T>::get().clone(); let mut candidates = Candidates::<T>::get().clone();
candidates.sort_by_key(|(c, _)| c.clone()); candidates.sort_by_key(|(c, _)| c.clone());
if Candidates::<T>::get() == candidates { if Candidates::<T>::get() == candidates {
Ok(()) Ok(())
} else { } else {
Err("try_state checks: Candidates must be always sorted by account ID") Err("try_state checks: Candidates must be always sorted by account ID".into())
} }
} }
// [`Candidates`] and [`RunnersUp`] state checks. Invariants: // [`Candidates`] and [`RunnersUp`] state checks. Invariants:
// - Candidates and runners-ups sets are disjoint. // - Candidates and runners-ups sets are disjoint.
fn try_state_candidates_runners_up_disjoint() -> Result<(), &'static str> { fn try_state_candidates_runners_up_disjoint() -> Result<(), TryRuntimeError> {
match Self::intersects(&Self::candidates_ids(), &Self::runners_up_ids()) { match Self::intersects(&Self::candidates_ids(), &Self::runners_up_ids()) {
true => Err("Candidates and runners up sets should always be disjoint"), true => Err("Candidates and runners up sets should always be disjoint".into()),
false => Ok(()), false => Ok(()),
} }
} }
@@ -1253,11 +1257,12 @@ impl<T: Config> Pallet<T> {
// [`Members`], [`Candidates`] and [`RunnersUp`] state checks. Invariants: // [`Members`], [`Candidates`] and [`RunnersUp`] state checks. Invariants:
// - Members and candidates sets are disjoint; // - Members and candidates sets are disjoint;
// - Members and runners-ups sets are disjoint. // - Members and runners-ups sets are disjoint.
fn try_state_members_disjoint() -> Result<(), &'static str> { fn try_state_members_disjoint() -> Result<(), TryRuntimeError> {
match Self::intersects(&Pallet::<T>::members_ids(), &Self::candidates_ids()) && match Self::intersects(&Pallet::<T>::members_ids(), &Self::candidates_ids()) &&
Self::intersects(&Pallet::<T>::members_ids(), &Self::runners_up_ids()) Self::intersects(&Pallet::<T>::members_ids(), &Self::runners_up_ids())
{ {
true => Err("Members set should be disjoint from candidates and runners-up sets"), true =>
Err("Members set should be disjoint from candidates and runners-up sets".into()),
false => Ok(()), false => Ok(()),
} }
} }
@@ -1265,14 +1270,14 @@ impl<T: Config> Pallet<T> {
// [`Members`], [`RunnersUp`] and approval stake state checks. Invariants: // [`Members`], [`RunnersUp`] and approval stake state checks. Invariants:
// - Selected members should have approval stake; // - Selected members should have approval stake;
// - Selected RunnersUp should have approval stake. // - Selected RunnersUp should have approval stake.
fn try_state_members_approval_stake() -> Result<(), &'static str> { fn try_state_members_approval_stake() -> Result<(), TryRuntimeError> {
match Members::<T>::get() match Members::<T>::get()
.iter() .iter()
.chain(RunnersUp::<T>::get().iter()) .chain(RunnersUp::<T>::get().iter())
.all(|s| s.stake != BalanceOf::<T>::zero()) .all(|s| s.stake != BalanceOf::<T>::zero())
{ {
true => Ok(()), true => Ok(()),
false => Err("Members and RunnersUp must have approval stake"), false => Err("Members and RunnersUp must have approval stake".into()),
} }
} }
+4 -1
View File
@@ -137,6 +137,9 @@ use sp_runtime::{
}; };
use sp_std::{marker::PhantomData, prelude::*}; use sp_std::{marker::PhantomData, prelude::*};
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
#[allow(dead_code)] #[allow(dead_code)]
const LOG_TARGET: &str = "runtime::executive"; const LOG_TARGET: &str = "runtime::executive";
@@ -338,7 +341,7 @@ where
/// `true`. Also, if set to `true`, it runs the `pre_upgrade` and `post_upgrade` hooks. /// `true`. Also, if set to `true`, it runs the `pre_upgrade` and `post_upgrade` hooks.
pub fn try_runtime_upgrade( pub fn try_runtime_upgrade(
checks: frame_try_runtime::UpgradeCheckSelect, checks: frame_try_runtime::UpgradeCheckSelect,
) -> Result<Weight, &'static str> { ) -> Result<Weight, TryRuntimeError> {
if checks.try_state() { if checks.try_state() {
let _guard = frame_support::StorageNoopGuard::default(); let _guard = frame_support::StorageNoopGuard::default();
<AllPalletsWithSystem as frame_support::traits::TryState<System::BlockNumber>>::try_state( <AllPalletsWithSystem as frame_support::traits::TryState<System::BlockNumber>>::try_state(
+5 -2
View File
@@ -91,6 +91,9 @@ pub mod pallet {
use sp_std::{prelude::*, vec::Vec}; use sp_std::{prelude::*, vec::Vec};
pub use weights::WeightInfo; pub use weights::WeightInfo;
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
#[derive(scale_info::TypeInfo, codec::Encode, codec::Decode, codec::MaxEncodedLen)] #[derive(scale_info::TypeInfo, codec::Encode, codec::Decode, codec::MaxEncodedLen)]
#[codec(mel_bound(T: Config))] #[codec(mel_bound(T: Config))]
#[scale_info(skip_type_params(T))] #[scale_info(skip_type_params(T))]
@@ -228,10 +231,10 @@ pub mod pallet {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn try_state(_n: T::BlockNumber) -> Result<(), &'static str> { fn try_state(_n: T::BlockNumber) -> Result<(), TryRuntimeError> {
// ensure that the value of `ErasToCheckPerBlock` is less than // ensure that the value of `ErasToCheckPerBlock` is less than
// `T::MaxErasToCheckPerBlock`. // `T::MaxErasToCheckPerBlock`.
assert!( ensure!(
ErasToCheckPerBlock::<T>::get() <= T::MaxErasToCheckPerBlock::get(), ErasToCheckPerBlock::<T>::get() <= T::MaxErasToCheckPerBlock::get(),
"the value of `ErasToCheckPerBlock` is greater than `T::MaxErasToCheckPerBlock`", "the value of `ErasToCheckPerBlock` is greater than `T::MaxErasToCheckPerBlock`",
); );
+15 -4
View File
@@ -25,6 +25,11 @@ pub mod v1 {
use sp_staking::EraIndex; use sp_staking::EraIndex;
use sp_std::prelude::*; use sp_std::prelude::*;
#[cfg(feature = "try-runtime")]
use frame_support::ensure;
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
pub struct MigrateToV1<T>(sp_std::marker::PhantomData<T>); pub struct MigrateToV1<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> { impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> {
fn on_runtime_upgrade() -> Weight { fn on_runtime_upgrade() -> Weight {
@@ -65,14 +70,20 @@ pub mod v1 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
assert_eq!(Pallet::<T>::on_chain_storage_version(), 0); ensure!(
Pallet::<T>::on_chain_storage_version() == 0,
"The onchain storage version must be zero for the migration to execute."
);
Ok(Default::default()) Ok(Default::default())
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
assert_eq!(Pallet::<T>::on_chain_storage_version(), 1); ensure!(
Pallet::<T>::on_chain_storage_version() == 1,
"The onchain version must be updated after the migration."
);
Ok(()) Ok(())
} }
} }
+2 -2
View File
@@ -43,7 +43,7 @@ pub mod v1 {
pub struct MigrateToV1<T>(sp_std::marker::PhantomData<T>); pub struct MigrateToV1<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> { impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> {
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
let onchain = Pallet::<T>::on_chain_storage_version(); let onchain = Pallet::<T>::on_chain_storage_version();
ensure!(onchain < 1, "this migration can be deleted"); ensure!(onchain < 1, "this migration can be deleted");
@@ -72,7 +72,7 @@ pub mod v1 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
let onchain = Pallet::<T>::on_chain_storage_version(); let onchain = Pallet::<T>::on_chain_storage_version();
ensure!(onchain < 2, "this migration needs to be removed"); ensure!(onchain < 2, "this migration needs to be removed");
ensure!(onchain == 1, "this migration needs to be run"); ensure!(onchain == 1, "this migration needs to be run");
+7 -4
View File
@@ -18,6 +18,9 @@
use super::*; use super::*;
use frame_support::{log, traits::OnRuntimeUpgrade}; use frame_support::{log, traits::OnRuntimeUpgrade};
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
pub mod v1 { pub mod v1 {
use frame_support::{pallet_prelude::*, weights::Weight}; use frame_support::{pallet_prelude::*, weights::Weight};
@@ -90,7 +93,7 @@ pub mod v1 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
let current_version = Pallet::<T>::current_storage_version(); let current_version = Pallet::<T>::current_storage_version();
let onchain_version = Pallet::<T>::on_chain_storage_version(); let onchain_version = Pallet::<T>::on_chain_storage_version();
ensure!(onchain_version == 0 && current_version == 1, "migration from version 0 to 1."); ensure!(onchain_version == 0 && current_version == 1, "migration from version 0 to 1.");
@@ -99,13 +102,13 @@ pub mod v1 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(prev_count: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(prev_count: Vec<u8>) -> Result<(), TryRuntimeError> {
let prev_count: u32 = Decode::decode(&mut prev_count.as_slice()).expect( let prev_count: u32 = Decode::decode(&mut prev_count.as_slice()).expect(
"the state parameter should be something that was generated by pre_upgrade", "the state parameter should be something that was generated by pre_upgrade",
); );
let post_count = Collection::<T>::iter().count() as u32; let post_count = Collection::<T>::iter().count() as u32;
assert_eq!( ensure!(
prev_count, post_count, prev_count == post_count,
"the records count before and after the migration should be the same" "the records count before and after the migration should be the same"
); );
+52 -25
View File
@@ -378,6 +378,9 @@ use sp_runtime::{
use sp_staking::{EraIndex, OnStakerSlash, StakingInterface}; use sp_staking::{EraIndex, OnStakerSlash, StakingInterface};
use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec};
#[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))]
use sp_runtime::TryRuntimeError;
/// The log target of this pallet. /// The log target of this pallet.
pub const LOG_TARGET: &str = "runtime::nomination-pools"; pub const LOG_TARGET: &str = "runtime::nomination-pools";
@@ -2626,7 +2629,7 @@ pub mod pallet {
#[pallet::hooks] #[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn try_state(_n: BlockNumberFor<T>) -> Result<(), &'static str> { fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
Self::do_try_state(u8::MAX) Self::do_try_state(u8::MAX)
} }
@@ -3055,7 +3058,7 @@ impl<T: Config> Pallet<T> {
/// multiple `level`s, where the higher the level, the more checks we performs. So, /// multiple `level`s, where the higher the level, the more checks we performs. So,
/// `try_state(255)` is the strongest sanity check, and `0` performs no checks. /// `try_state(255)` is the strongest sanity check, and `0` performs no checks.
#[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))]
pub fn do_try_state(level: u8) -> Result<(), &'static str> { pub fn do_try_state(level: u8) -> Result<(), TryRuntimeError> {
if level.is_zero() { if level.is_zero() {
return Ok(()) return Ok(())
} }
@@ -3063,12 +3066,24 @@ impl<T: Config> Pallet<T> {
// result in the same set of keys, in the same order. // result in the same set of keys, in the same order.
let bonded_pools = BondedPools::<T>::iter_keys().collect::<Vec<_>>(); let bonded_pools = BondedPools::<T>::iter_keys().collect::<Vec<_>>();
let reward_pools = RewardPools::<T>::iter_keys().collect::<Vec<_>>(); let reward_pools = RewardPools::<T>::iter_keys().collect::<Vec<_>>();
assert_eq!(bonded_pools, reward_pools); ensure!(
bonded_pools == reward_pools,
"`BondedPools` and `RewardPools` must all have the EXACT SAME key-set."
);
assert!(Metadata::<T>::iter_keys().all(|k| bonded_pools.contains(&k))); ensure!(
assert!(SubPoolsStorage::<T>::iter_keys().all(|k| bonded_pools.contains(&k))); SubPoolsStorage::<T>::iter_keys().all(|k| bonded_pools.contains(&k)),
"`SubPoolsStorage` must be a subset of the above superset."
);
ensure!(
Metadata::<T>::iter_keys().all(|k| bonded_pools.contains(&k)),
"`Metadata` keys must be a subset of the above superset."
);
assert!(MaxPools::<T>::get().map_or(true, |max| bonded_pools.len() <= (max as usize))); ensure!(
MaxPools::<T>::get().map_or(true, |max| bonded_pools.len() <= (max as usize)),
Error::<T>::MaxPools
);
for id in reward_pools { for id in reward_pools {
let account = Self::create_reward_account(id); let account = Self::create_reward_account(id);
@@ -3088,9 +3103,9 @@ impl<T: Config> Pallet<T> {
let mut pools_members = BTreeMap::<PoolId, u32>::new(); let mut pools_members = BTreeMap::<PoolId, u32>::new();
let mut pools_members_pending_rewards = BTreeMap::<PoolId, BalanceOf<T>>::new(); let mut pools_members_pending_rewards = BTreeMap::<PoolId, BalanceOf<T>>::new();
let mut all_members = 0u32; let mut all_members = 0u32;
PoolMembers::<T>::iter().for_each(|(_, d)| { PoolMembers::<T>::iter().try_for_each(|(_, d)| -> Result<(), TryRuntimeError> {
let bonded_pool = BondedPools::<T>::get(d.pool_id).unwrap(); let bonded_pool = BondedPools::<T>::get(d.pool_id).unwrap();
assert!(!d.total_points().is_zero(), "no member should have zero points: {d:?}"); ensure!(!d.total_points().is_zero(), "No member should have zero points");
*pools_members.entry(d.pool_id).or_default() += 1; *pools_members.entry(d.pool_id).or_default() += 1;
all_members += 1; all_members += 1;
@@ -3103,9 +3118,11 @@ impl<T: Config> Pallet<T> {
let pending_rewards = d.pending_rewards(current_rc).unwrap(); let pending_rewards = d.pending_rewards(current_rc).unwrap();
*pools_members_pending_rewards.entry(d.pool_id).or_default() += pending_rewards; *pools_members_pending_rewards.entry(d.pool_id).or_default() += pending_rewards;
} // else this pool has been heavily slashed and cannot have any rewards anymore. } // else this pool has been heavily slashed and cannot have any rewards anymore.
});
RewardPools::<T>::iter_keys().for_each(|id| { Ok(())
})?;
RewardPools::<T>::iter_keys().try_for_each(|id| -> Result<(), TryRuntimeError> {
// the sum of the pending rewards must be less than the leftover balance. Since the // the sum of the pending rewards must be less than the leftover balance. Since the
// reward math rounds down, we might accumulate some dust here. // reward math rounds down, we might accumulate some dust here.
log!( log!(
@@ -3115,30 +3132,40 @@ impl<T: Config> Pallet<T> {
pools_members_pending_rewards.get(&id), pools_members_pending_rewards.get(&id),
RewardPool::<T>::current_balance(id) RewardPool::<T>::current_balance(id)
); );
assert!( ensure!(
RewardPool::<T>::current_balance(id) >= RewardPool::<T>::current_balance(id) >=
pools_members_pending_rewards.get(&id).copied().unwrap_or_default() pools_members_pending_rewards.get(&id).copied().unwrap_or_default(),
) "The sum of the pending rewards must be less than the leftover balance."
}); );
Ok(())
BondedPools::<T>::iter().for_each(|(id, inner)| { })?;
let bonded_pool = BondedPool { id, inner };
assert_eq!( BondedPools::<T>::iter().try_for_each(|(id, inner)| -> Result<(), TryRuntimeError> {
pools_members.get(&id).copied().unwrap_or_default(), let bonded_pool = BondedPool { id, inner };
bonded_pool.member_counter ensure!(
pools_members.get(&id).copied().unwrap_or_default() ==
bonded_pool.member_counter,
"Each `BondedPool.member_counter` must be equal to the actual count of members of this pool"
);
ensure!(
MaxPoolMembersPerPool::<T>::get()
.map_or(true, |max| bonded_pool.member_counter <= max),
Error::<T>::MaxPoolMembers
); );
assert!(MaxPoolMembersPerPool::<T>::get()
.map_or(true, |max| bonded_pool.member_counter <= max));
let depositor = PoolMembers::<T>::get(&bonded_pool.roles.depositor).unwrap(); let depositor = PoolMembers::<T>::get(&bonded_pool.roles.depositor).unwrap();
assert!( ensure!(
bonded_pool.is_destroying_and_only_depositor(depositor.active_points()) || bonded_pool.is_destroying_and_only_depositor(depositor.active_points()) ||
depositor.active_points() >= MinCreateBond::<T>::get(), depositor.active_points() >= MinCreateBond::<T>::get(),
"depositor must always have MinCreateBond stake in the pool, except for when the \ "depositor must always have MinCreateBond stake in the pool, except for when the \
pool is being destroyed and the depositor is the last member", pool is being destroyed and the depositor is the last member",
); );
}); Ok(())
assert!(MaxPoolMembers::<T>::get().map_or(true, |max| all_members <= max)); })?;
ensure!(
MaxPoolMembers::<T>::get().map_or(true, |max| all_members <= max),
Error::<T>::MaxPoolMembers
);
if level <= 1 { if level <= 1 {
return Ok(()) return Ok(())
@@ -20,6 +20,9 @@ use crate::log;
use frame_support::traits::OnRuntimeUpgrade; use frame_support::traits::OnRuntimeUpgrade;
use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
pub mod v1 { pub mod v1 {
use super::*; use super::*;
@@ -100,9 +103,12 @@ pub mod v1 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
// new version must be set. // new version must be set.
assert_eq!(Pallet::<T>::on_chain_storage_version(), 1); ensure!(
Pallet::<T>::on_chain_storage_version() == 1,
"The onchain version must be updated after the migration."
);
Pallet::<T>::try_state(frame_system::Pallet::<T>::block_number())?; Pallet::<T>::try_state(frame_system::Pallet::<T>::block_number())?;
Ok(()) Ok(())
} }
@@ -352,38 +358,47 @@ pub mod v2 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
// all reward accounts must have more than ED. // all reward accounts must have more than ED.
RewardPools::<T>::iter().for_each(|(id, _)| { RewardPools::<T>::iter().try_for_each(|(id, _)| -> Result<(), TryRuntimeError> {
assert!( ensure!(
T::Currency::free_balance(&Pallet::<T>::create_reward_account(id)) >= T::Currency::free_balance(&Pallet::<T>::create_reward_account(id)) >=
T::Currency::minimum_balance() T::Currency::minimum_balance(),
) "Reward accounts must have greater balance than ED."
}); );
Ok(())
})?;
Ok(Vec::new()) Ok(Vec::new())
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
// new version must be set. // new version must be set.
assert_eq!(Pallet::<T>::on_chain_storage_version(), 2); ensure!(
Pallet::<T>::on_chain_storage_version() == 2,
"The onchain version must be updated after the migration."
);
// no reward or bonded pool has been skipped. // no reward or bonded pool has been skipped.
assert_eq!(RewardPools::<T>::iter().count() as u32, RewardPools::<T>::count()); ensure!(
assert_eq!(BondedPools::<T>::iter().count() as u32, BondedPools::<T>::count()); RewardPools::<T>::iter().count() as u32 == RewardPools::<T>::count(),
"The count of reward pools must remain the same after the migration."
);
ensure!(
BondedPools::<T>::iter().count() as u32 == BondedPools::<T>::count(),
"The count of reward pools must remain the same after the migration."
);
// all reward pools must have exactly ED in them. This means no reward can be claimed, // all reward pools must have exactly ED in them. This means no reward can be claimed,
// and that setting reward counters all over the board to zero will work henceforth. // and that setting reward counters all over the board to zero will work henceforth.
RewardPools::<T>::iter().for_each(|(id, _)| { RewardPools::<T>::iter().try_for_each(|(id, _)| -> Result<(), TryRuntimeError> {
assert_eq!( ensure!(
RewardPool::<T>::current_balance(id), RewardPool::<T>::current_balance(id) == Zero::zero(),
Zero::zero(), "Reward pool balance must be zero.",
"reward pool({}) balance is {:?}",
id,
RewardPool::<T>::current_balance(id)
); );
}); Ok(())
})?;
log!(info, "post upgrade hook for MigrateToV2 executed."); log!(info, "post upgrade hook for MigrateToV2 executed.");
Ok(()) Ok(())
@@ -435,7 +450,7 @@ pub mod v3 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
ensure!( ensure!(
Pallet::<T>::current_storage_version() > Pallet::<T>::on_chain_storage_version(), Pallet::<T>::current_storage_version() > Pallet::<T>::on_chain_storage_version(),
"the on_chain version is equal or more than the current one" "the on_chain version is equal or more than the current one"
@@ -444,7 +459,7 @@ pub mod v3 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
ensure!( ensure!(
Metadata::<T>::iter_keys().all(|id| BondedPools::<T>::contains_key(&id)), Metadata::<T>::iter_keys().all(|id| BondedPools::<T>::contains_key(&id)),
"not all of the stale metadata has been removed" "not all of the stale metadata has been removed"
@@ -535,7 +550,7 @@ pub mod v4 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
ensure!( ensure!(
Pallet::<T>::current_storage_version() > Pallet::<T>::on_chain_storage_version(), Pallet::<T>::current_storage_version() > Pallet::<T>::on_chain_storage_version(),
"the on_chain version is equal or more than the current one" "the on_chain version is equal or more than the current one"
@@ -544,7 +559,7 @@ pub mod v4 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
// ensure all BondedPools items now contain an `inner.commission: Commission` field. // ensure all BondedPools items now contain an `inner.commission: Commission` field.
ensure!( ensure!(
BondedPools::<T>::iter().all(|(_, inner)| inner.commission.current.is_none() && BondedPools::<T>::iter().all(|(_, inner)| inner.commission.current.is_none() &&
@@ -620,7 +635,7 @@ pub mod v5 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
ensure!( ensure!(
Pallet::<T>::current_storage_version() > Pallet::<T>::on_chain_storage_version(), Pallet::<T>::current_storage_version() > Pallet::<T>::on_chain_storage_version(),
"the on_chain version is equal or more than the current one" "the on_chain version is equal or more than the current one"
@@ -654,7 +669,7 @@ pub mod v5 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(data: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(data: Vec<u8>) -> Result<(), TryRuntimeError> {
let old_rpool_values: u64 = Decode::decode(&mut &data[..]).unwrap(); let old_rpool_values: u64 = Decode::decode(&mut &data[..]).unwrap();
let rpool_keys = RewardPools::<T>::iter_keys().count() as u64; let rpool_keys = RewardPools::<T>::iter_keys().count() as u64;
let rpool_values = RewardPools::<T>::iter_values().count() as u64; let rpool_values = RewardPools::<T>::iter_values().count() as u64;
@@ -4269,10 +4269,7 @@ mod create {
assert!(!BondedPools::<Runtime>::contains_key(2)); assert!(!BondedPools::<Runtime>::contains_key(2));
assert!(!RewardPools::<Runtime>::contains_key(2)); assert!(!RewardPools::<Runtime>::contains_key(2));
assert!(!PoolMembers::<Runtime>::contains_key(11)); assert!(!PoolMembers::<Runtime>::contains_key(11));
assert_err!( assert_err!(StakingMock::active_stake(&next_pool_stash), "balance not found");
StakingMock::active_stake(&next_pool_stash),
DispatchError::Other("balance not found")
);
Balances::make_free_balance_be(&11, StakingMock::minimum_nominator_bond() + ed); Balances::make_free_balance_be(&11, StakingMock::minimum_nominator_bond() + ed);
assert_ok!(Pools::create( assert_ok!(Pools::create(
+4 -2
View File
@@ -29,6 +29,8 @@ use sp_std::vec::Vec;
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
use frame_support::ensure; use frame_support::ensure;
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
mod v0 { mod v0 {
use super::*; use super::*;
@@ -51,7 +53,7 @@ pub mod v1 {
pub struct MigrateToV1<T>(sp_std::marker::PhantomData<T>); pub struct MigrateToV1<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> { impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> {
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
let onchain = Pallet::<T>::on_chain_storage_version(); let onchain = Pallet::<T>::on_chain_storage_version();
ensure!(onchain < 1, "pallet_offences::MigrateToV1 migration can be deleted"); ensure!(onchain < 1, "pallet_offences::MigrateToV1 migration can be deleted");
@@ -81,7 +83,7 @@ pub mod v1 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
let onchain = Pallet::<T>::on_chain_storage_version(); let onchain = Pallet::<T>::on_chain_storage_version();
ensure!(onchain == 1, "pallet_offences::MigrateToV1 needs to be run"); ensure!(onchain == 1, "pallet_offences::MigrateToV1 needs to be run");
ensure!( ensure!(
+9 -4
View File
@@ -24,6 +24,11 @@ use frame_support::{
}; };
use sp_std::collections::btree_map::BTreeMap; 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. /// The log target.
const TARGET: &'static str = "runtime::preimage::migration::v1"; const TARGET: &'static str = "runtime::preimage::migration::v1";
@@ -78,8 +83,8 @@ pub mod v1 {
impl<T: Config> OnRuntimeUpgrade for Migration<T> { impl<T: Config> OnRuntimeUpgrade for Migration<T> {
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
assert_eq!(StorageVersion::get::<Pallet<T>>(), 0, "can only upgrade from version 0"); ensure!(StorageVersion::get::<Pallet<T>>() == 0, "can only upgrade from version 0");
let images = v0::image_count::<T>().expect("v0 storage corrupted"); let images = v0::image_count::<T>().expect("v0 storage corrupted");
log::info!(target: TARGET, "Migrating {} images", &images); log::info!(target: TARGET, "Migrating {} images", &images);
@@ -148,7 +153,7 @@ pub mod v1 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(state: Vec<u8>) -> DispatchResult {
let old_images: u32 = let old_images: u32 =
Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed"); Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed");
let new_images = image_count::<T>().expect("V1 storage corrupted"); let new_images = image_count::<T>().expect("V1 storage corrupted");
@@ -161,7 +166,7 @@ pub mod v1 {
old_images old_images
); );
} }
assert_eq!(StorageVersion::get::<Pallet<T>>(), 1, "must upgrade"); ensure!(StorageVersion::get::<Pallet<T>>() == 1, "must upgrade");
Ok(()) Ok(())
} }
} }
+8 -8
View File
@@ -22,6 +22,9 @@ use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
use frame_support::{pallet_prelude::*, storage_alias, traits::OnRuntimeUpgrade}; use frame_support::{pallet_prelude::*, storage_alias, traits::OnRuntimeUpgrade};
use log; use log;
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
/// Initial version of storage types. /// Initial version of storage types.
pub mod v0 { pub mod v0 {
use super::*; use super::*;
@@ -95,9 +98,9 @@ pub mod v1 {
pub struct MigrateV0ToV1<T, I = ()>(PhantomData<(T, I)>); pub struct MigrateV0ToV1<T, I = ()>(PhantomData<(T, I)>);
impl<T: Config<I>, I: 'static> OnRuntimeUpgrade for MigrateV0ToV1<T, I> { impl<T: Config<I>, I: 'static> OnRuntimeUpgrade for MigrateV0ToV1<T, I> {
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
let onchain_version = Pallet::<T, I>::on_chain_storage_version(); let onchain_version = Pallet::<T, I>::on_chain_storage_version();
assert_eq!(onchain_version, 0, "migration from version 0 to 1."); ensure!(onchain_version == 0, "migration from version 0 to 1.");
let referendum_count = v0::ReferendumInfoFor::<T, I>::iter().count(); let referendum_count = v0::ReferendumInfoFor::<T, I>::iter().count();
log::info!( log::info!(
target: TARGET, target: TARGET,
@@ -147,16 +150,13 @@ pub mod v1 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(state: Vec<u8>) -> Result<(), TryRuntimeError> {
let onchain_version = Pallet::<T, I>::on_chain_storage_version(); let onchain_version = Pallet::<T, I>::on_chain_storage_version();
assert_eq!(onchain_version, 1, "must upgrade from version 0 to 1."); ensure!(onchain_version == 1, "must upgrade from version 0 to 1.");
let pre_referendum_count: u32 = Decode::decode(&mut &state[..]) let pre_referendum_count: u32 = Decode::decode(&mut &state[..])
.expect("failed to decode the state from pre-upgrade."); .expect("failed to decode the state from pre-upgrade.");
let post_referendum_count = ReferendumInfoFor::<T, I>::iter().count() as u32; let post_referendum_count = ReferendumInfoFor::<T, I>::iter().count() as u32;
assert_eq!( ensure!(post_referendum_count == pre_referendum_count, "must migrate all referendums.");
post_referendum_count, pre_referendum_count,
"must migrate all referendums."
);
log::info!(target: TARGET, "migrated all referendums."); log::info!(target: TARGET, "migrated all referendums.");
Ok(()) Ok(())
} }
+15 -12
View File
@@ -20,6 +20,9 @@
use super::*; use super::*;
use frame_support::traits::OnRuntimeUpgrade; use frame_support::traits::OnRuntimeUpgrade;
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
/// The log target. /// The log target.
const TARGET: &'static str = "runtime::scheduler::migration"; const TARGET: &'static str = "runtime::scheduler::migration";
@@ -97,8 +100,8 @@ pub mod v3 {
impl<T: Config<Hash = PreimageHash>> OnRuntimeUpgrade for MigrateToV4<T> { impl<T: Config<Hash = PreimageHash>> OnRuntimeUpgrade for MigrateToV4<T> {
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
assert_eq!(StorageVersion::get::<Pallet<T>>(), 3, "Can only upgrade from version 3"); ensure!(StorageVersion::get::<Pallet<T>>() == 3, "Can only upgrade from version 3");
let agendas = Agenda::<T>::iter_keys().count() as u32; let agendas = Agenda::<T>::iter_keys().count() as u32;
let decodable_agendas = Agenda::<T>::iter_values().count() as u32; let decodable_agendas = Agenda::<T>::iter_values().count() as u32;
@@ -125,7 +128,7 @@ pub mod v3 {
agenda.len(), agenda.len(),
max_scheduled_per_block, max_scheduled_per_block,
); );
return Err("Agenda would overflow `MaxScheduledPerBlock`.") return Err("Agenda would overflow `MaxScheduledPerBlock`.".into())
} }
} }
// Check that bounding the calls will not overflow `MAX_LENGTH`. // Check that bounding the calls will not overflow `MAX_LENGTH`.
@@ -142,7 +145,7 @@ pub mod v3 {
block_number, block_number,
l, l,
); );
return Err("Call is too large.") return Err("Call is too large.".into())
} }
}, },
_ => (), _ => (),
@@ -169,12 +172,12 @@ pub mod v3 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(state: Vec<u8>) -> Result<(), TryRuntimeError> {
assert_eq!(StorageVersion::get::<Pallet<T>>(), 4, "Must upgrade"); ensure!(StorageVersion::get::<Pallet<T>>() == 4, "Must upgrade");
// Check that everything decoded fine. // Check that everything decoded fine.
for k in crate::Agenda::<T>::iter_keys() { for k in crate::Agenda::<T>::iter_keys() {
assert!(crate::Agenda::<T>::try_get(k).is_ok(), "Cannot decode V4 Agenda"); ensure!(crate::Agenda::<T>::try_get(k).is_ok(), "Cannot decode V4 Agenda");
} }
let old_agendas: u32 = let old_agendas: u32 =
@@ -210,7 +213,7 @@ pub mod v4 {
impl<T: Config> OnRuntimeUpgrade for CleanupAgendas<T> { impl<T: Config> OnRuntimeUpgrade for CleanupAgendas<T> {
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
assert_eq!( assert_eq!(
StorageVersion::get::<Pallet<T>>(), StorageVersion::get::<Pallet<T>>(),
4, 4,
@@ -285,8 +288,8 @@ pub mod v4 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(state: Vec<u8>) -> Result<(), TryRuntimeError> {
assert_eq!(StorageVersion::get::<Pallet<T>>(), 4, "Version must not change"); ensure!(StorageVersion::get::<Pallet<T>>() == 4, "Version must not change");
let (old_agendas, non_empty_agendas): (u32, u32) = let (old_agendas, non_empty_agendas): (u32, u32) =
Decode::decode(&mut state.as_ref()).expect("Must decode pre_upgrade state"); Decode::decode(&mut state.as_ref()).expect("Must decode pre_upgrade state");
@@ -305,7 +308,7 @@ pub mod v4 {
old_agendas, new_agendas old_agendas, new_agendas
), ),
} }
assert_eq!(new_agendas, non_empty_agendas, "Expected to keep all non-empty agendas"); ensure!(new_agendas == non_empty_agendas, "Expected to keep all non-empty agendas");
Ok(()) Ok(())
} }
@@ -496,7 +499,7 @@ mod test {
// The pre_upgrade hook fails: // The pre_upgrade hook fails:
let err = v3::MigrateToV4::<Test>::pre_upgrade().unwrap_err(); let err = v3::MigrateToV4::<Test>::pre_upgrade().unwrap_err();
assert!(err.contains("Call is too large")); assert!(err == "Call is too large".into());
// But the migration itself works: // But the migration itself works:
let _w = v3::MigrateToV4::<Test>::on_runtime_upgrade(); let _w = v3::MigrateToV4::<Test>::on_runtime_upgrade();
+19 -10
View File
@@ -23,6 +23,11 @@ use frame_support::{
traits::OnRuntimeUpgrade, traits::OnRuntimeUpgrade,
}; };
#[cfg(feature = "try-runtime")]
use frame_support::ensure;
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
/// Used for release versioning upto v12. /// Used for release versioning upto v12.
/// ///
/// Obsolete from v13. Keeping around to make encoding/decoding of old migration code easier. /// Obsolete from v13. Keeping around to make encoding/decoding of old migration code easier.
@@ -58,7 +63,7 @@ pub mod v13 {
pub struct MigrateToV13<T>(sp_std::marker::PhantomData<T>); pub struct MigrateToV13<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> OnRuntimeUpgrade for MigrateToV13<T> { impl<T: Config> OnRuntimeUpgrade for MigrateToV13<T> {
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
frame_support::ensure!( frame_support::ensure!(
StorageVersion::<T>::get() == ObsoleteReleases::V12_0_0, StorageVersion::<T>::get() == ObsoleteReleases::V12_0_0,
"Required v12 before upgrading to v13" "Required v12 before upgrading to v13"
@@ -84,7 +89,7 @@ pub mod v13 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
frame_support::ensure!( frame_support::ensure!(
Pallet::<T>::on_chain_storage_version() == 13, Pallet::<T>::on_chain_storage_version() == 13,
"v13 not applied" "v13 not applied"
@@ -114,7 +119,7 @@ pub mod v12 {
pub struct MigrateToV12<T>(sp_std::marker::PhantomData<T>); pub struct MigrateToV12<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> OnRuntimeUpgrade for MigrateToV12<T> { impl<T: Config> OnRuntimeUpgrade for MigrateToV12<T> {
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
frame_support::ensure!( frame_support::ensure!(
StorageVersion::<T>::get() == ObsoleteReleases::V11_0_0, StorageVersion::<T>::get() == ObsoleteReleases::V11_0_0,
"Expected v11 before upgrading to v12" "Expected v11 before upgrading to v12"
@@ -146,7 +151,7 @@ pub mod v12 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
frame_support::ensure!( frame_support::ensure!(
StorageVersion::<T>::get() == ObsoleteReleases::V12_0_0, StorageVersion::<T>::get() == ObsoleteReleases::V12_0_0,
"v12 not applied" "v12 not applied"
@@ -170,7 +175,7 @@ pub mod v11 {
for MigrateToV11<T, P, N> for MigrateToV11<T, P, N>
{ {
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
frame_support::ensure!( frame_support::ensure!(
StorageVersion::<T>::get() == ObsoleteReleases::V10_0_0, StorageVersion::<T>::get() == ObsoleteReleases::V10_0_0,
"must upgrade linearly" "must upgrade linearly"
@@ -217,7 +222,7 @@ pub mod v11 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
frame_support::ensure!( frame_support::ensure!(
StorageVersion::<T>::get() == ObsoleteReleases::V11_0_0, StorageVersion::<T>::get() == ObsoleteReleases::V11_0_0,
"wrong version after the upgrade" "wrong version after the upgrade"
@@ -332,7 +337,7 @@ pub mod v9 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
frame_support::ensure!( frame_support::ensure!(
StorageVersion::<T>::get() == ObsoleteReleases::V8_0_0, StorageVersion::<T>::get() == ObsoleteReleases::V8_0_0,
"must upgrade linearly" "must upgrade linearly"
@@ -343,17 +348,21 @@ pub mod v9 {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(prev_count: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(prev_count: Vec<u8>) -> Result<(), TryRuntimeError> {
let prev_count: u32 = Decode::decode(&mut prev_count.as_slice()).expect( let prev_count: u32 = Decode::decode(&mut prev_count.as_slice()).expect(
"the state parameter should be something that was generated by pre_upgrade", "the state parameter should be something that was generated by pre_upgrade",
); );
let post_count = T::VoterList::count(); let post_count = T::VoterList::count();
let validators = Validators::<T>::count(); let validators = Validators::<T>::count();
assert!(post_count == prev_count + validators); ensure!(
post_count == prev_count + validators,
"`VoterList` count after the migration must equal to the sum of \
previous count and the current number of validators"
);
frame_support::ensure!( frame_support::ensure!(
StorageVersion::<T>::get() == ObsoleteReleases::V9_0_0, StorageVersion::<T>::get() == ObsoleteReleases::V9_0_0,
"must upgrade " "must upgrade"
); );
Ok(()) Ok(())
} }
+28 -16
View File
@@ -51,6 +51,11 @@ use crate::{
use super::{pallet::*, STAKING_ID}; use super::{pallet::*, STAKING_ID};
#[cfg(feature = "try-runtime")]
use frame_support::ensure;
#[cfg(any(test, feature = "try-runtime"))]
use sp_runtime::TryRuntimeError;
/// The maximum number of iterations that we do whilst iterating over `T::VoterList` in /// The maximum number of iterations that we do whilst iterating over `T::VoterList` in
/// `get_npos_voters`. /// `get_npos_voters`.
/// ///
@@ -1467,7 +1472,7 @@ impl<T: Config> SortedListProvider<T::AccountId> for UseValidatorsMap<T> {
0 0
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn try_state() -> Result<(), &'static str> { fn try_state() -> Result<(), TryRuntimeError> {
Ok(()) Ok(())
} }
@@ -1544,7 +1549,7 @@ impl<T: Config> SortedListProvider<T::AccountId> for UseNominatorsAndValidatorsM
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn try_state() -> Result<(), &'static str> { fn try_state() -> Result<(), TryRuntimeError> {
Ok(()) Ok(())
} }
@@ -1713,7 +1718,7 @@ impl<T: Config> StakingInterface for Pallet<T> {
#[cfg(any(test, feature = "try-runtime"))] #[cfg(any(test, feature = "try-runtime"))]
impl<T: Config> Pallet<T> { impl<T: Config> Pallet<T> {
pub(crate) fn do_try_state(_: BlockNumberFor<T>) -> Result<(), &'static str> { pub(crate) fn do_try_state(_: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
ensure!( ensure!(
T::VoterList::iter() T::VoterList::iter()
.all(|x| <Nominators<T>>::contains_key(&x) || <Validators<T>>::contains_key(&x)), .all(|x| <Nominators<T>>::contains_key(&x) || <Validators<T>>::contains_key(&x)),
@@ -1726,7 +1731,7 @@ impl<T: Config> Pallet<T> {
Self::check_count() Self::check_count()
} }
fn check_count() -> Result<(), &'static str> { fn check_count() -> Result<(), TryRuntimeError> {
ensure!( ensure!(
<T as Config>::VoterList::count() == <T as Config>::VoterList::count() ==
Nominators::<T>::count() + Validators::<T>::count(), Nominators::<T>::count() + Validators::<T>::count(),
@@ -1739,18 +1744,19 @@ impl<T: Config> Pallet<T> {
ensure!( ensure!(
ValidatorCount::<T>::get() <= ValidatorCount::<T>::get() <=
<T::ElectionProvider as frame_election_provider_support::ElectionProviderBase>::MaxWinners::get(), <T::ElectionProvider as frame_election_provider_support::ElectionProviderBase>::MaxWinners::get(),
"validator count exceeded election max winners" Error::<T>::TooManyValidators
); );
Ok(()) Ok(())
} }
fn check_ledgers() -> Result<(), &'static str> { fn check_ledgers() -> Result<(), TryRuntimeError> {
Bonded::<T>::iter() Bonded::<T>::iter()
.map(|(_, ctrl)| Self::ensure_ledger_consistent(ctrl)) .map(|(_, ctrl)| Self::ensure_ledger_consistent(ctrl))
.collect::<Result<_, _>>() .collect::<Result<Vec<_>, _>>()?;
Ok(())
} }
fn check_exposures() -> Result<(), &'static str> { fn check_exposures() -> Result<(), TryRuntimeError> {
// a check per validator to ensure the exposure struct is always sane. // a check per validator to ensure the exposure struct is always sane.
let era = Self::active_era().unwrap().index; let era = Self::active_era().unwrap().index;
ErasStakers::<T>::iter_prefix_values(era) ErasStakers::<T>::iter_prefix_values(era)
@@ -1766,10 +1772,10 @@ impl<T: Config> Pallet<T> {
); );
Ok(()) Ok(())
}) })
.collect::<Result<_, _>>() .collect::<Result<(), TryRuntimeError>>()
} }
fn check_nominators() -> Result<(), &'static str> { fn check_nominators() -> Result<(), TryRuntimeError> {
// a check per nominator to ensure their entire stake is correctly distributed. Will only // a check per nominator to ensure their entire stake is correctly distributed. Will only
// kick-in if the nomination was submitted before the current era. // kick-in if the nomination was submitted before the current era.
let era = Self::active_era().unwrap().index; let era = Self::active_era().unwrap().index;
@@ -1783,27 +1789,33 @@ impl<T: Config> Pallet<T> {
} }
}, },
) )
.map(|nominator| { .map(|nominator| -> Result<(), TryRuntimeError> {
// must be bonded. // must be bonded.
Self::ensure_is_stash(&nominator)?; Self::ensure_is_stash(&nominator)?;
let mut sum = BalanceOf::<T>::zero(); let mut sum = BalanceOf::<T>::zero();
T::SessionInterface::validators() T::SessionInterface::validators()
.iter() .iter()
.map(|v| Self::eras_stakers(era, v)) .map(|v| Self::eras_stakers(era, v))
.map(|e| { .map(|e| -> Result<(), TryRuntimeError> {
let individual = let individual =
e.others.iter().filter(|e| e.who == nominator).collect::<Vec<_>>(); e.others.iter().filter(|e| e.who == nominator).collect::<Vec<_>>();
let len = individual.len(); let len = individual.len();
match len { match len {
0 => { /* not supporting this validator at all. */ }, 0 => { /* not supporting this validator at all. */ },
1 => sum += individual[0].value, 1 => sum += individual[0].value,
_ => return Err("nominator cannot back a validator more than once."), _ =>
return Err(
"nominator cannot back a validator more than once.".into()
),
}; };
Ok(()) Ok(())
}) })
.collect::<Result<_, _>>() .collect::<Result<Vec<_>, _>>()?;
Ok(())
}) })
.collect::<Result<_, _>>() .collect::<Result<Vec<_>, _>>()?;
Ok(())
} }
fn ensure_is_stash(who: &T::AccountId) -> Result<(), &'static str> { fn ensure_is_stash(who: &T::AccountId) -> Result<(), &'static str> {
@@ -1811,7 +1823,7 @@ impl<T: Config> Pallet<T> {
Ok(()) Ok(())
} }
fn ensure_ledger_consistent(ctrl: T::AccountId) -> Result<(), &'static str> { fn ensure_ledger_consistent(ctrl: T::AccountId) -> Result<(), TryRuntimeError> {
// ensures ledger.total == ledger.active + sum(ledger.unlocking). // ensures ledger.total == ledger.active + sum(ledger.unlocking).
let ledger = Self::ledger(ctrl.clone()).ok_or("Not a controller.")?; let ledger = Self::ledger(ctrl.clone()).ok_or("Not a controller.")?;
let real_total: BalanceOf<T> = let real_total: BalanceOf<T> =
+1 -1
View File
@@ -805,7 +805,7 @@ pub mod pallet {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn try_state(n: BlockNumberFor<T>) -> Result<(), &'static str> { fn try_state(n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
Self::do_try_state(n) Self::do_try_state(n)
} }
} }
@@ -105,7 +105,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream {
current_version, current_version,
); );
return Err("On chain and current storage version do not match. Missing runtime upgrade?"); return Err("On chain and current storage version do not match. Missing runtime upgrade?".into());
} }
} }
} else { } else {
@@ -128,7 +128,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream {
); );
return Err("On chain storage version set, while the pallet doesn't \ return Err("On chain storage version set, while the pallet doesn't \
have the `#[pallet::storage_version(VERSION)]` attribute."); have the `#[pallet::storage_version(VERSION)]` attribute.".into());
} }
} }
}; };
@@ -211,7 +211,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<#frame_support::sp_std::vec::Vec<u8>, &'static str> { fn pre_upgrade() -> Result<#frame_support::sp_std::vec::Vec<u8>, #frame_support::sp_runtime::TryRuntimeError> {
< <
Self Self
as as
@@ -220,7 +220,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(state: #frame_support::sp_std::vec::Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(state: #frame_support::sp_std::vec::Vec<u8>) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> {
#post_storage_version_check #post_storage_version_check
< <
@@ -268,7 +268,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream {
fn try_state( fn try_state(
n: <T as #frame_system::Config>::BlockNumber, n: <T as #frame_system::Config>::BlockNumber,
_s: #frame_support::traits::TryStateSelect _s: #frame_support::traits::TryStateSelect
) -> Result<(), &'static str> { ) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> {
#log_try_state #log_try_state
< <
Self as #frame_support::traits::Hooks< Self as #frame_support::traits::Hooks<
+5 -5
View File
@@ -2097,7 +2097,7 @@ macro_rules! decl_module {
fn try_state( fn try_state(
_: <$trait_instance as $system::Config>::BlockNumber, _: <$trait_instance as $system::Config>::BlockNumber,
_: $crate::traits::TryStateSelect, _: $crate::traits::TryStateSelect,
) -> Result<(), &'static str> { ) -> Result<(), $crate::sp_runtime::TryRuntimeError> {
let pallet_name = << let pallet_name = <<
$trait_instance $trait_instance
as as
@@ -2144,12 +2144,12 @@ macro_rules! decl_module {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<$crate::sp_std::vec::Vec<u8>, &'static str> { fn pre_upgrade() -> Result<$crate::sp_std::vec::Vec<u8>, $crate::sp_runtime::TryRuntimeError> {
Ok($crate::sp_std::vec::Vec::new()) Ok($crate::sp_std::vec::Vec::new())
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_: $crate::sp_std::vec::Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_: $crate::sp_std::vec::Vec<u8>) -> Result<(), $crate::sp_runtime::TryRuntimeError> {
Ok(()) Ok(())
} }
} }
@@ -2182,12 +2182,12 @@ macro_rules! decl_module {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<$crate::sp_std::vec::Vec<u8>, &'static str> { fn pre_upgrade() -> Result<$crate::sp_std::vec::Vec<u8>, $crate::sp_runtime::TryRuntimeError> {
Ok($crate::sp_std::vec::Vec::new()) Ok($crate::sp_std::vec::Vec::new())
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_: $crate::sp_std::vec::Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_: $crate::sp_std::vec::Vec<u8>) -> Result<(), $crate::sp_runtime::TryRuntimeError> {
Ok(()) Ok(())
} }
} }
+3 -3
View File
@@ -184,7 +184,7 @@ impl<P: Get<&'static str>, DbWeight: Get<RuntimeDbWeight>> frame_support::traits
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
let hashed_prefix = twox_128(P::get().as_bytes()); let hashed_prefix = twox_128(P::get().as_bytes());
match contains_prefixed_key(&hashed_prefix) { match contains_prefixed_key(&hashed_prefix) {
true => log::info!("Found {} keys pre-removal 👀", P::get()), true => log::info!("Found {} keys pre-removal 👀", P::get()),
@@ -197,12 +197,12 @@ impl<P: Get<&'static str>, DbWeight: Get<RuntimeDbWeight>> frame_support::traits
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
let hashed_prefix = twox_128(P::get().as_bytes()); let hashed_prefix = twox_128(P::get().as_bytes());
match contains_prefixed_key(&hashed_prefix) { match contains_prefixed_key(&hashed_prefix) {
true => { true => {
log::error!("{} has keys remaining post-removal ❗", P::get()); log::error!("{} has keys remaining post-removal ❗", P::get());
return Err("Keys remaining post-removal, this should never happen 🚨") return Err("Keys remaining post-removal, this should never happen 🚨".into())
}, },
false => log::info!("No {} keys found post-removal 🎉", P::get()), false => log::info!("No {} keys found post-removal 🎉", P::get()),
}; };
+14 -11
View File
@@ -22,6 +22,9 @@ use impl_trait_for_tuples::impl_for_tuples;
use sp_runtime::traits::AtLeast32BitUnsigned; use sp_runtime::traits::AtLeast32BitUnsigned;
use sp_std::prelude::*; use sp_std::prelude::*;
#[cfg(feature = "try-runtime")]
use sp_runtime::TryRuntimeError;
/// The block initialization trait. /// The block initialization trait.
/// ///
/// Implementing this lets you express what should happen for your pallet when the block is /// Implementing this lets you express what should happen for your pallet when the block is
@@ -136,7 +139,7 @@ pub trait OnRuntimeUpgrade {
/// Same as `on_runtime_upgrade`, but perform the optional `pre_upgrade` and `post_upgrade` as /// Same as `on_runtime_upgrade`, but perform the optional `pre_upgrade` and `post_upgrade` as
/// well. /// well.
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn try_on_runtime_upgrade(checks: bool) -> Result<Weight, &'static str> { fn try_on_runtime_upgrade(checks: bool) -> Result<Weight, TryRuntimeError> {
let maybe_state = if checks { let maybe_state = if checks {
let _guard = frame_support::StorageNoopGuard::default(); let _guard = frame_support::StorageNoopGuard::default();
let state = Self::pre_upgrade()?; let state = Self::pre_upgrade()?;
@@ -167,7 +170,7 @@ pub trait OnRuntimeUpgrade {
/// This hook must not write to any state, as it would make the main `on_runtime_upgrade` path /// This hook must not write to any state, as it would make the main `on_runtime_upgrade` path
/// inaccurate. /// inaccurate.
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
Ok(Vec::new()) Ok(Vec::new())
} }
@@ -182,7 +185,7 @@ pub trait OnRuntimeUpgrade {
/// This hook must not write to any state, as it would make the main `on_runtime_upgrade` path /// This hook must not write to any state, as it would make the main `on_runtime_upgrade` path
/// inaccurate. /// inaccurate.
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
Ok(()) Ok(())
} }
} }
@@ -201,7 +204,7 @@ impl OnRuntimeUpgrade for Tuple {
/// consecutive migrations for the same pallet without errors. Therefore pre and post upgrade /// consecutive migrations for the same pallet without errors. Therefore pre and post upgrade
/// hooks for tuples are a noop. /// hooks for tuples are a noop.
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn try_on_runtime_upgrade(checks: bool) -> Result<Weight, &'static str> { fn try_on_runtime_upgrade(checks: bool) -> Result<Weight, TryRuntimeError> {
let mut weight = Weight::zero(); let mut weight = Weight::zero();
let mut errors = Vec::new(); let mut errors = Vec::new();
@@ -224,12 +227,12 @@ impl OnRuntimeUpgrade for Tuple {
errors.iter().for_each(|err| { errors.iter().for_each(|err| {
log::error!( log::error!(
target: "try-runtime", target: "try-runtime",
"{}", "{:?}",
err err
); );
}); });
return Err("Detected multiple errors while executing `try_on_runtime_upgrade`, check the logs!") return Err("Detected multiple errors while executing `try_on_runtime_upgrade`, check the logs!".into())
} }
Ok(weight) Ok(weight)
@@ -305,7 +308,7 @@ pub trait Hooks<BlockNumber> {
/// ///
/// This hook should not alter any storage. /// This hook should not alter any storage.
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn try_state(_n: BlockNumber) -> Result<(), &'static str> { fn try_state(_n: BlockNumber) -> Result<(), TryRuntimeError> {
Ok(()) Ok(())
} }
@@ -317,7 +320,7 @@ pub trait Hooks<BlockNumber> {
/// ///
/// This hook is never meant to be executed on-chain but is meant to be used by testing tools. /// This hook is never meant to be executed on-chain but is meant to be used by testing tools.
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
Ok(Vec::new()) Ok(Vec::new())
} }
@@ -329,7 +332,7 @@ pub trait Hooks<BlockNumber> {
/// ///
/// This hook is never meant to be executed on-chain but is meant to be used by testing tools. /// This hook is never meant to be executed on-chain but is meant to be used by testing tools.
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
Ok(()) Ok(())
} }
@@ -411,13 +414,13 @@ mod tests {
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
Pre::mutate(|s| s.push(stringify!($name))); Pre::mutate(|s| s.push(stringify!($name)));
Ok(Vec::new()) Ok(Vec::new())
} }
#[cfg(feature = "try-runtime")] #[cfg(feature = "try-runtime")]
fn post_upgrade(_: Vec<u8>) -> Result<(), &'static str> { fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
Post::mutate(|s| s.push(stringify!($name))); Post::mutate(|s| s.push(stringify!($name)));
Ok(()) Ok(())
} }
@@ -19,6 +19,7 @@
use impl_trait_for_tuples::impl_for_tuples; use impl_trait_for_tuples::impl_for_tuples;
use sp_arithmetic::traits::AtLeast32BitUnsigned; use sp_arithmetic::traits::AtLeast32BitUnsigned;
use sp_runtime::TryRuntimeError;
use sp_std::prelude::*; use sp_std::prelude::*;
/// Which state tests to execute. /// Which state tests to execute.
@@ -129,7 +130,7 @@ impl core::str::FromStr for UpgradeCheckSelect {
/// This hook should not alter any storage. /// This hook should not alter any storage.
pub trait TryState<BlockNumber> { pub trait TryState<BlockNumber> {
/// Execute the state checks. /// Execute the state checks.
fn try_state(_: BlockNumber, _: Select) -> Result<(), &'static str>; fn try_state(_: BlockNumber, _: Select) -> Result<(), TryRuntimeError>;
} }
#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] #[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))]
@@ -139,7 +140,7 @@ impl<BlockNumber: Clone + sp_std::fmt::Debug + AtLeast32BitUnsigned> TryState<Bl
for Tuple for Tuple
{ {
for_tuples!( where #( Tuple: crate::traits::PalletInfoAccess )* ); for_tuples!( where #( Tuple: crate::traits::PalletInfoAccess )* );
fn try_state(n: BlockNumber, targets: Select) -> Result<(), &'static str> { fn try_state(n: BlockNumber, targets: Select) -> Result<(), TryRuntimeError> {
match targets { match targets {
Select::None => Ok(()), Select::None => Ok(()),
Select::All => { Select::All => {
@@ -148,7 +149,7 @@ impl<BlockNumber: Clone + sp_std::fmt::Debug + AtLeast32BitUnsigned> TryState<Bl
result result
}, },
Select::RoundRobin(len) => { Select::RoundRobin(len) => {
let functions: &[fn(BlockNumber, Select) -> Result<(), &'static str>] = let functions: &[fn(BlockNumber, Select) -> Result<(), TryRuntimeError>] =
&[for_tuples!(#( Tuple::try_state ),*)]; &[for_tuples!(#( Tuple::try_state ),*)];
let skip = n.clone() % (functions.len() as u32).into(); let skip = n.clone() % (functions.len() as u32).into();
let skip: u32 = let skip: u32 =
@@ -163,7 +164,7 @@ impl<BlockNumber: Clone + sp_std::fmt::Debug + AtLeast32BitUnsigned> TryState<Bl
Select::Only(ref pallet_names) => { Select::Only(ref pallet_names) => {
let try_state_fns: &[( let try_state_fns: &[(
&'static str, &'static str,
fn(BlockNumber, Select) -> Result<(), &'static str>, fn(BlockNumber, Select) -> Result<(), TryRuntimeError>,
)] = &[for_tuples!( )] = &[for_tuples!(
#( (<Tuple as crate::traits::PalletInfoAccess>::name(), Tuple::try_state) ),* #( (<Tuple as crate::traits::PalletInfoAccess>::name(), Tuple::try_state) ),*
)]; )];
+11 -6
View File
@@ -2110,9 +2110,11 @@ fn post_runtime_upgrade_detects_storage_version_issues() {
// Call `on_genesis` to put the storage version of `Example` into the storage. // Call `on_genesis` to put the storage version of `Example` into the storage.
Example::on_genesis(); Example::on_genesis();
// The version isn't changed, we should detect it. // The version isn't changed, we should detect it.
assert!(Executive::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost) assert!(
.unwrap_err() Executive::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost).unwrap_err() ==
.contains("On chain and current storage version do not match")); "On chain and current storage version do not match. Missing runtime upgrade?"
.into()
);
}); });
TestExternalities::default().execute_with(|| { TestExternalities::default().execute_with(|| {
@@ -2138,9 +2140,12 @@ fn post_runtime_upgrade_detects_storage_version_issues() {
// `CustomUpgradePallet4` will set a storage version for `Example4` while this doesn't has // `CustomUpgradePallet4` will set a storage version for `Example4` while this doesn't has
// any storage version "enabled". // any storage version "enabled".
assert!(ExecutiveWithUpgradePallet4::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost) assert!(
.unwrap_err() ExecutiveWithUpgradePallet4::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost)
.contains("On chain storage version set, while the pallet doesn't")); .unwrap_err() == "On chain storage version set, while the pallet \
doesn't have the `#[pallet::storage_version(VERSION)]` attribute."
.into()
);
}); });
} }
+3
View File
@@ -784,6 +784,9 @@ pub type ApplyExtrinsicResult =
pub type ApplyExtrinsicResultWithInfo<T> = pub type ApplyExtrinsicResultWithInfo<T> =
Result<DispatchResultWithInfo<T>, transaction_validity::TransactionValidityError>; Result<DispatchResultWithInfo<T>, transaction_validity::TransactionValidityError>;
/// The error type used as return type in try runtime hooks.
pub type TryRuntimeError = DispatchError;
/// Verify a signature on an encoded value in a lazy manner. This can be /// Verify a signature on an encoded value in a lazy manner. This can be
/// an optimization if the signature scheme has an "unsigned" escape hash. /// an optimization if the signature scheme has an "unsigned" escape hash.
pub fn verify_encoded_lazy<V: Verify, T: codec::Encode>( pub fn verify_encoded_lazy<V: Verify, T: codec::Encode>(
@@ -122,10 +122,10 @@
//! //!
//! ```ignore //! ```ignore
//! #[cfg(feature = "try-runtime")] //! #[cfg(feature = "try-runtime")]
//! fn pre_upgrade() -> Result<Vec<u8>, &'static str> {} //! fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {}
//! //!
//! #[cfg(feature = "try-runtime")] //! #[cfg(feature = "try-runtime")]
//! fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> {} //! fn post_upgrade(state: Vec<u8>) -> Result<(), TryRuntimeError> {}
//! ``` //! ```
//! //!
//! (The pallet macro syntax will support this simply as a part of `#[pallet::hooks]`). //! (The pallet macro syntax will support this simply as a part of `#[pallet::hooks]`).
@@ -141,7 +141,7 @@
//! //!
//! ```ignore //! ```ignore
//! #[cfg(feature = "try-runtime")] //! #[cfg(feature = "try-runtime")]
//! fn try_state(_: BlockNumber) -> Result<(), &'static str> {} //! fn try_state(_: BlockNumber) -> Result<(), TryRuntimeError> {}
//! ``` //! ```
//! //!
//! which is called on numerous code paths in the try-runtime tool. These checks should ensure that //! which is called on numerous code paths in the try-runtime tool. These checks should ensure that