Runtime State Test + Integration with try-runtime (#10174)

* add missing version to dependencies

* Huh

* add features more

* more fixing

* last touches

* it all finally works

* remove some feature gates

* remove unused

* fix old macro

* make it work again

* fmt

* remove unused import

* ".git/.scripts/fmt.sh" 1

* Cleanup more

* fix and rename everything

* a few clippy fixes

* Add try-runtime feature

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* small fixes

* fmt

* Update bin/node-template/runtime/src/lib.rs

* fix build

* Update utils/frame/try-runtime/cli/src/lib.rs

Co-authored-by: David <dvdplm@gmail.com>

* Update utils/frame/try-runtime/cli/src/commands/execute_block.rs

Co-authored-by: David <dvdplm@gmail.com>

* address all review comments

* fix typos

* revert spec change

* last touches

* update docs

* fmt

* remove some debug_assertions

* fmt

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: command-bot <>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: David <dvdplm@gmail.com>
This commit is contained in:
Kian Paimani
2022-09-01 11:33:22 +01:00
committed by GitHub
parent d8e951758c
commit f67c06ce22
39 changed files with 651 additions and 209 deletions
+2 -2
View File
@@ -153,7 +153,7 @@ pub mod v8 {
Nominators::<T>::iter().map(|(id, _)| id),
Pallet::<T>::weight_of_fn(),
);
debug_assert_eq!(T::VoterList::sanity_check(), Ok(()));
debug_assert_eq!(T::VoterList::try_state(), Ok(()));
StorageVersion::<T>::put(crate::Releases::V8_0_0);
crate::log!(
@@ -170,7 +170,7 @@ pub mod v8 {
#[cfg(feature = "try-runtime")]
pub fn post_migrate<T: Config>() -> Result<(), &'static str> {
T::VoterList::sanity_check().map_err(|_| "VoterList is not in a sane state.")?;
T::VoterList::try_state().map_err(|_| "VoterList is not in a sane state.")?;
crate::log!(info, "👜 staking bags-list migration passes POST migrate checks ✅",);
Ok(())
}
+102 -5
View File
@@ -789,7 +789,6 @@ impl<T: Config> Pallet<T> {
Nominators::<T>::count() + Validators::<T>::count(),
T::VoterList::count()
);
debug_assert_eq!(T::VoterList::sanity_check(), Ok(()));
}
/// This function will remove a nominator from the `Nominators` storage map,
@@ -809,7 +808,6 @@ impl<T: Config> Pallet<T> {
false
};
debug_assert_eq!(T::VoterList::sanity_check(), Ok(()));
debug_assert_eq!(
Nominators::<T>::count() + Validators::<T>::count(),
T::VoterList::count()
@@ -837,7 +835,6 @@ impl<T: Config> Pallet<T> {
Nominators::<T>::count() + Validators::<T>::count(),
T::VoterList::count()
);
debug_assert_eq!(T::VoterList::sanity_check(), Ok(()));
}
/// This function will remove a validator from the `Validators` storage map.
@@ -856,7 +853,6 @@ impl<T: Config> Pallet<T> {
false
};
debug_assert_eq!(T::VoterList::sanity_check(), Ok(()));
debug_assert_eq!(
Nominators::<T>::count() + Validators::<T>::count(),
T::VoterList::count()
@@ -1369,7 +1365,7 @@ impl<T: Config> SortedListProvider<T::AccountId> for UseNominatorsAndValidatorsM
// nothing to do upon regenerate.
0
}
fn sanity_check() -> Result<(), &'static str> {
fn try_state() -> Result<(), &'static str> {
Ok(())
}
@@ -1452,3 +1448,104 @@ impl<T: Config> StakingInterface for Pallet<T> {
Nominators::<T>::get(who).map(|n| n.targets.into_inner())
}
}
#[cfg(feature = "try-runtime")]
impl<T: Config> Pallet<T> {
pub(crate) fn do_try_state(_: BlockNumberFor<T>) -> Result<(), &'static str> {
T::VoterList::try_state()?;
Self::check_nominators()?;
Self::check_exposures()?;
Self::check_ledgers()?;
Self::check_count()
}
fn check_count() -> Result<(), &'static str> {
ensure!(
<T as Config>::VoterList::count() ==
Nominators::<T>::count() + Validators::<T>::count(),
"wrong external count"
);
Ok(())
}
fn check_ledgers() -> Result<(), &'static str> {
Bonded::<T>::iter()
.map(|(_, ctrl)| Self::ensure_ledger_consistent(ctrl))
.collect::<Result<_, _>>()
}
fn check_exposures() -> Result<(), &'static str> {
// a check per validator to ensure the exposure struct is always sane.
let era = Self::active_era().unwrap().index;
ErasStakers::<T>::iter_prefix_values(era)
.map(|expo| {
ensure!(
expo.total ==
expo.own +
expo.others
.iter()
.map(|e| e.value)
.fold(Zero::zero(), |acc, x| acc + x),
"wrong total exposure.",
);
Ok(())
})
.collect::<Result<_, _>>()
}
fn check_nominators() -> Result<(), &'static str> {
// 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.
let era = Self::active_era().unwrap().index;
<Nominators<T>>::iter()
.filter_map(
|(nominator, nomination)| {
if nomination.submitted_in > era {
Some(nominator)
} else {
None
}
},
)
.map(|nominator| {
// must be bonded.
Self::ensure_is_stash(&nominator)?;
let mut sum = BalanceOf::<T>::zero();
T::SessionInterface::validators()
.iter()
.map(|v| Self::eras_stakers(era, v))
.map(|e| {
let individual =
e.others.iter().filter(|e| e.who == nominator).collect::<Vec<_>>();
let len = individual.len();
match len {
0 => { /* not supporting this validator at all. */ },
1 => sum += individual[0].value,
_ => return Err("nominator cannot back a validator more than once."),
};
Ok(())
})
.collect::<Result<_, _>>()
})
.collect::<Result<_, _>>()
}
fn ensure_is_stash(who: &T::AccountId) -> Result<(), &'static str> {
ensure!(Self::bonded(who).is_some(), "Not a stash.");
Ok(())
}
fn ensure_ledger_consistent(ctrl: T::AccountId) -> Result<(), &'static str> {
// ensures ledger.total == ledger.active + sum(ledger.unlocking).
let ledger = Self::ledger(ctrl.clone()).ok_or("Not a controller.")?;
let real_total: BalanceOf<T> =
ledger.unlocking.iter().fold(ledger.active, |a, c| a + c.value);
ensure!(real_total == ledger.total, "ledger.total corrupt");
if !(ledger.active >= T::Currency::minimum_balance() || ledger.active.is_zero()) {
log!(warn, "ledger.active less than ED: {:?}, {:?}", ctrl, ledger)
}
Ok(())
}
}
+5 -1
View File
@@ -744,6 +744,11 @@ pub mod pallet {
);
}
}
#[cfg(feature = "try-runtime")]
fn try_state(n: BlockNumberFor<T>) -> Result<(), &'static str> {
Self::do_try_state(n)
}
}
#[pallet::call]
@@ -856,7 +861,6 @@ pub mod pallet {
if T::VoterList::contains(&stash) {
let _ =
T::VoterList::on_update(&stash, Self::weight_of(&ledger.stash)).defensive();
debug_assert_eq!(T::VoterList::sanity_check(), Ok(()));
}
Self::deposit_event(Event::<T>::Bonded(stash, extra));