Execute try-state at end of each test to ensure pallet data integrity (#12453)

* execute try-state at end of tests

* run post condition only with try runtime

* Revert "run post condition only with try runtime"

This reverts commit 7db0ecf7eaa2ee5afa5a995487b73d023ba3bcd9.

* voterlist contains validators as well

* fmt

* simplify

* fmt

Co-authored-by: parity-processbot <>
This commit is contained in:
Ankan
2022-10-17 18:44:42 +02:00
committed by GitHub
parent bb175f0373
commit ca15fe7e3d
2 changed files with 7 additions and 106 deletions
+4 -104
View File
@@ -18,9 +18,7 @@
//! Test utilities
use crate::{self as pallet_staking, *};
use frame_election_provider_support::{
onchain, SequentialPhragmen, SortedListProvider, VoteWeight,
};
use frame_election_provider_support::{onchain, SequentialPhragmen, VoteWeight};
use frame_support::{
assert_ok, parameter_types,
traits::{
@@ -548,108 +546,10 @@ impl ExtBuilder {
sp_tracing::try_init_simple();
let mut ext = self.build();
ext.execute_with(test);
ext.execute_with(post_conditions);
}
}
fn post_conditions() {
check_nominators();
check_exposures();
check_ledgers();
check_count();
}
fn check_count() {
let nominator_count = Nominators::<Test>::iter_keys().count() as u32;
let validator_count = Validators::<Test>::iter().count() as u32;
assert_eq!(nominator_count, Nominators::<Test>::count());
assert_eq!(validator_count, Validators::<Test>::count());
// the voters that the `VoterList` list is storing for us.
let external_voters = <Test as Config>::VoterList::count();
assert_eq!(external_voters, nominator_count + validator_count);
}
fn check_ledgers() {
// check the ledger of all stakers.
Bonded::<Test>::iter().for_each(|(_, ctrl)| assert_ledger_consistent(ctrl))
}
fn check_exposures() {
// a check per validator to ensure the exposure struct is always sane.
let era = active_era();
ErasStakers::<Test>::iter_prefix_values(era).for_each(|expo| {
assert_eq!(
expo.total as u128,
expo.own as u128 + expo.others.iter().map(|e| e.value as u128).sum::<u128>(),
"wrong total exposure.",
);
})
}
fn check_nominators() {
// 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 = active_era();
<Nominators<Test>>::iter()
.filter_map(
|(nominator, nomination)| {
if nomination.submitted_in > era {
Some(nominator)
} else {
None
}
},
)
.for_each(|nominator| {
// must be bonded.
assert_is_stash(nominator);
let mut sum = 0;
Session::validators()
.iter()
.map(|v| Staking::eras_stakers(era, v))
.for_each(|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,
_ => panic!("nominator cannot back a validator more than once."),
};
ext.execute_with(|| {
Staking::do_try_state(System::block_number()).unwrap();
});
let nominator_stake = Staking::slashable_balance_of(&nominator);
// a nominator cannot over-spend.
assert!(
nominator_stake >= sum,
"failed: Nominator({}) stake({}) >= sum divided({})",
nominator,
nominator_stake,
sum,
);
let diff = nominator_stake - sum;
assert!(diff < 100);
});
}
fn assert_is_stash(acc: AccountId) {
assert!(Staking::bonded(&acc).is_some(), "Not a stash.");
}
fn assert_ledger_consistent(ctrl: AccountId) {
// ensures ledger.total == ledger.active + sum(ledger.unlocking).
let ledger = Staking::ledger(ctrl).expect("Not a controller.");
let real_total: Balance = ledger.unlocking.iter().fold(ledger.active, |a, c| a + c.value);
assert_eq!(real_total, ledger.total);
assert!(
ledger.active >= Balances::minimum_balance() || ledger.active == 0,
"{}: active ledger amount ({}) must be greater than ED {}",
ctrl,
ledger.active,
Balances::minimum_balance()
);
}
}
pub(crate) fn active_era() -> EraIndex {
+3 -2
View File
@@ -1552,11 +1552,12 @@ impl<T: Config> StakingInterface for Pallet<T> {
}
}
#[cfg(feature = "try-runtime")]
#[cfg(any(test, feature = "try-runtime"))]
impl<T: Config> Pallet<T> {
pub(crate) fn do_try_state(_: BlockNumberFor<T>) -> Result<(), &'static str> {
ensure!(
T::VoterList::iter().all(|x| <Nominators<T>>::contains_key(&x)),
T::VoterList::iter()
.all(|x| <Nominators<T>>::contains_key(&x) || <Validators<T>>::contains_key(&x)),
"VoterList contains non-nominators"
);
T::VoterList::try_state()?;