Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
55cf648
force rebag for unbond, rebond, and bond_extra
emostov Aug 10, 2021
ff55aba
nit
emostov Aug 10, 2021
759e309
Merge branch 'zeke-voter-bags-module' into zeke-bags-list-staking-ben…
emostov Aug 10, 2021
37c8436
Improve utils
emostov Aug 10, 2021
f3b8c8a
fmt
emostov Aug 10, 2021
a96ce8e
Try merge origin master
emostov Aug 10, 2021
7b6686a
nits
emostov Aug 10, 2021
1a91ead
Merge branch 'zeke-voter-bags-module' into zeke-bags-list-staking-ben…
emostov Aug 10, 2021
804151c
Move generate_bags to its own pallet
emostov Aug 10, 2021
34e2194
Get runtime-benchmarks feature setup with prepare_on_update_benchmark
emostov Aug 10, 2021
dec1e8b
Withdraw unbonded kill working
emostov Aug 12, 2021
a354364
Nominate bench working
emostov Aug 13, 2021
02a218f
some cleanup
emostov Aug 13, 2021
77ab8af
WIP
emostov Aug 13, 2021
da38ccc
Try merge zeke-voter-bags-module
emostov Aug 13, 2021
ecdb7e8
update to check head pre & post conditions
emostov Aug 13, 2021
d3bbd18
Add some post condition verification stuff for on_remove
emostov Aug 13, 2021
1b82579
Update nominate
emostov Aug 13, 2021
19ebb8a
fmt
emostov Aug 13, 2021
e79ad98
Improvements
emostov Aug 13, 2021
cd3a63c
Fix build
emostov Aug 14, 2021
7d18281
fix build with polkadot companion
emostov Aug 14, 2021
644e3bf
Update frame/bags-list/src/list/tests.rs
emostov Aug 16, 2021
a2ddcb4
Try merge feature
emostov Aug 20, 2021
ded3b49
move generate-bag from frame to utils
emostov Aug 20, 2021
624cfe9
wip
emostov Aug 24, 2021
d34a1fd
Merge remote-tracking branch 'origin' into zeke-bags-list-staking-ben…
emostov Aug 24, 2021
d1f5d44
refactor WIP
emostov Aug 24, 2021
76688e7
Merge branch 'zeke-voter-bags-module' of github.com:paritytech/substr…
kianenigma Aug 24, 2021
dbb95db
WIP save
emostov Aug 24, 2021
0cd7103
Refactor working
emostov Aug 24, 2021
aea5554
some variable renaming
emostov Aug 24, 2021
033b717
WIP: prepare to remove head checks
emostov Aug 24, 2021
d08d858
Finish MvP refactor
emostov Aug 24, 2021
d5601ad
Merge branch 'zeke-voter-bags-module' into zeke-bags-list-staking-ben…
emostov Aug 24, 2021
8cacd0c
Some cleanup
emostov Aug 24, 2021
5aa980c
Soem more cleanup
emostov Aug 24, 2021
4f56f74
Merge branch 'zeke-bags-list-staking-benchmarks' of https://github.co…
emostov Aug 24, 2021
efbf22d
save
emostov Aug 26, 2021
532ffff
fix a lot of stuff
kianenigma Aug 26, 2021
24c2767
Update client/db/src/bench.rs
emostov Aug 26, 2021
6b91371
Apply suggestions from code review
emostov Aug 26, 2021
0317ea1
Apply suggestions from code review
emostov Aug 26, 2021
4faf565
Fix some issues that came from trying to merge comments on github
emostov Aug 27, 2021
41a50db
some small changes
emostov Aug 27, 2021
6277454
simplify it
kianenigma Aug 27, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
update to check head pre & post conditions
  • Loading branch information
emostov committed Aug 13, 2021
commit ecdb7e85a178c4f7fd334fa3ae2512bc3087cc8d
7 changes: 2 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 19 additions & 3 deletions frame/bags-list/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ pub mod pallet {
/// the procedure given above, then the constant ratio is equal to 2.
/// - If `BagThresholds::get().len() == 200`, and the thresholds are determined according to
/// the procedure given above, then the constant ratio is approximately equal to 1.248.
/// - If the threshold list begins `[1, 2, 3, ...]`, then an id with weight 0 or 1 will
/// fall into bag 0, an id with weight 2 will fall into bag 1, etc.
/// - If the threshold list begins `[1, 2, 3, ...]`, then an id with weight 0 or 1 will fall
/// into bag 0, an id with weight 2 will fall into bag 1, etc.
///
/// # Migration
///
Expand Down Expand Up @@ -265,7 +265,23 @@ impl<T: Config> SortedListProvider<T::AccountId> for Pallet<T> {
}

#[cfg(feature = "runtime-benchmarks")]
fn is_in_bag(id: &T::AccountId, weight: VoteWeight) -> bool {
fn is_in_bag(id: &T::AccountId, weight: VoteWeight, _: bool) -> bool {
use frame_support::traits::Get;
let info = T::BagThresholds::get()
.into_iter()
.chain(std::iter::once(&VoteWeight::MAX)) // assumes this is not an explicit threshold
.filter_map(|t| {
list::Bag::<T>::get(*t)
.map(|bag| (*t, bag.iter().map(|n| n.id().clone()).collect::<Vec<_>>()))
})
.collect::<Vec<_>>();
println!("bags info {:#?}", info);

list::Bag::<T>::get(list::notional_bag_for::<T>(weight)).unwrap().contains(id)
}

#[cfg(feature = "runtime-benchmarks")]
fn is_bag_head(id: &T::AccountId, weight: VoteWeight, _: bool) -> bool {
list::Bag::<T>::get(list::notional_bag_for::<T>(weight)).unwrap().head_id() == Some(id)
}
}
13 changes: 9 additions & 4 deletions frame/bags-list/src/list/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,10 @@ impl<T: Config> List<T> {
/// Postconditions:
///
/// - All `bag_upper` currently in storage are members of `T::BagThresholds`.
/// - No id is changed unless required to by the difference between the old threshold list
/// and the new.
/// - ids whose bags change at all are implicitly rebagged into the appropriate bag in the
/// new threshold set.
/// - No id is changed unless required to by the difference between the old threshold list and
/// the new.
/// - ids whose bags change at all are implicitly rebagged into the appropriate bag in the new
/// threshold set.
#[allow(dead_code)]
pub fn migrate(old_thresholds: &[VoteWeight]) -> u32 {
// we can't check all preconditions, but we can check one
Expand Down Expand Up @@ -590,6 +590,11 @@ impl<T: Config> Bag<T> {
pub(crate) fn contains(&self, id: &T::AccountId) -> bool {
self.iter().any(|n| n.id() == id)
}

#[cfg(feature = "runtime-benchmarks")]
pub(crate) fn head_id(&self) -> Option<&T::AccountId> {
self.head.as_ref()
}
}

/// A Node is the fundamental element comprising the doubly-linked list described by `Bag`.
Expand Down
3 changes: 2 additions & 1 deletion frame/bags-list/src/list/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ fn migrate_works() {
(15, vec![710]), // nodes in range 11 ..= 15 move from bag 20 to bag 15
(20, vec![711]),
(1000, vec![2, 3, 4]),
(10_000, vec![712]), // nodes in range 1_001 ..= 2_000 move from bag 2_000 to bag 10_000
(10_000, vec![712]), /* nodes in range 1_001 ..= 2_000 move from bag 2_000
* to bag 10_000 */
]
);
});
Expand Down
15 changes: 11 additions & 4 deletions frame/election-provider-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,11 +332,18 @@ pub trait SortedListProvider<AccountId> {
/// Sanity check internal state of list. Only meant for debug compilation.
fn sanity_check() -> Result<(), &'static str>;

/// If the voter is in the notional bag for the given weight.
/// Wether the voter is in the notional bag for the given weight. Only relevant for
/// implementations that use bags.
#[cfg(any(feature = "runtime-benchmarks", test))]
fn is_in_bag(_: &AccountId, _: VoteWeight) -> bool {
// TODO problematic if this always returns true
true // default to true for impls that don't have a bag.
fn is_in_bag(_: &AccountId, _: VoteWeight, mock: bool) -> bool {
mock // default to returning mock value for values that are not relevant
}

/// Wether the voter is the head of the notional bag for the given weight. Only relevant for
/// implementations that use bags.
#[cfg(any(feature = "runtime-benchmarks", test))]
fn is_bag_head(_: &AccountId, _: VoteWeight, mock: bool) -> bool {
mock // default to returning mock value for values that are not relevant
}
}

Expand Down
4 changes: 2 additions & 2 deletions frame/generate-bags/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
//! ```
//!
//! 2. Write a little program to generate the definitions. This can be a near-identical copy of
//! `substrate/frame/bags-list/generate-bags`. This program exists only to hook together the runtime
//! definitions with the various calculations here.
//! `substrate/frame/bags-list/generate-bags`. This program exists only to hook together the
//! runtime definitions with the various calculations here.
//!
//! 3. Run that program:
//!
Expand Down
96 changes: 70 additions & 26 deletions frame/staking/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,16 @@ struct RebagScenario<T: Config> {
/// Controller of the Stash that is expected to be rebagged.
origin_controller1: T::AccountId,
origin_stash2: T::AccountId,
origin_bag_thresh: BalanceOf<T>,
dest_bag_thresh: BalanceOf<T>,
origin_thresh_as_vote: VoteWeight,
dest_thresh_as_vote: VoteWeight,
}

impl<T: Config> RebagScenario<T> {
/// An expensive rebag scenario:
///
/// - the node to be rebagged (r) is the head of a bag that has at least one other node. The bag
/// itself will need to be read and written to update its head. The node pointed to by
/// r.next will need to be read and written as it will need to have its prev pointer updated.
/// itself will need to be read and written to update its head. The node pointed to by r.next
/// will need to be read and written as it will need to have its prev pointer updated.
///
/// - the destination bag has at least one node, which will need its next pointer updated.
fn new(
Expand Down Expand Up @@ -198,27 +198,75 @@ impl<T: Config> RebagScenario<T> {
)?;
Staking::<T>::nominate(RawOrigin::Signed(dest_controller1).into(), validators)?;

let total_issuance = T::Currency::total_issuance();
let origin_thresh_as_vote = T::CurrencyToVote::to_vote(origin_bag_thresh, total_issuance);
let dest_thresh_as_vote = T::CurrencyToVote::to_vote(dest_bag_thresh, total_issuance);

Ok(RebagScenario {
dest_stash1,
origin_stash1,
origin_controller1,
origin_stash2,
origin_bag_thresh,
dest_bag_thresh,
origin_thresh_as_vote,
dest_thresh_as_vote,
})
}

fn assert_preconditions(&self) {
let total_issuance = T::Currency::total_issuance();
let dest_thresh_as_vote = T::CurrencyToVote::to_vote(self.dest_bag_thresh, total_issuance);
let origin_thresh_as_vote = T::CurrencyToVote::to_vote(self.origin_bag_thresh, total_issuance);
fn check_preconditions(&self) {
let RebagScenario {
dest_stash1,
origin_stash2,
dest_thresh_as_vote,
origin_thresh_as_vote,
..
} = self;
// destination stash is not in the origin bag.
assert!(!T::SortedListProvider::is_in_bag(&dest_stash1, *origin_thresh_as_vote, false));
// origin stash2 is in the origin bag
assert!(T::SortedListProvider::is_in_bag(&origin_stash2, *origin_thresh_as_vote, true));
// head checks implicitly check that dest stash1 and origin stash1 are in the correct bags.
self.check_head_preconditions();
}

fn check_postconditions(&self) {
let RebagScenario {
dest_stash1,
origin_stash1,
dest_thresh_as_vote,
origin_thresh_as_vote,
..
} = self;
// destination stash is not in the origin bag
assert!(!T::SortedListProvider::is_in_bag(&self.dest_stash1, origin_thresh_as_vote));
assert!(!T::SortedListProvider::is_in_bag(&dest_stash1, *origin_thresh_as_vote, false));
// and is in the destination bag.
assert!(T::SortedListProvider::is_in_bag(&self.dest_stash1, dest_thresh_as_vote));
// the origin stash is in the origin bag.
assert!(T::SortedListProvider::is_in_bag(&self.origin_stash1, origin_thresh_as_vote));
assert!(T::SortedListProvider::is_in_bag(&dest_stash1, *dest_thresh_as_vote, true));
// the origin stash is in the destination bag.
assert!(T::SortedListProvider::is_in_bag(&origin_stash1, *dest_thresh_as_vote, true));
self.check_head_postconditions();
}

fn check_head_preconditions(&self) {
let RebagScenario {
dest_stash1,
origin_stash1,
dest_thresh_as_vote,
origin_thresh_as_vote,
..
} = self;
assert!(T::SortedListProvider::is_bag_head(&dest_stash1, *dest_thresh_as_vote, true));
assert!(T::SortedListProvider::is_bag_head(&origin_stash1, *origin_thresh_as_vote, true));
}

fn check_head_postconditions(&self) {
let RebagScenario {
dest_stash1,
origin_stash2,
dest_thresh_as_vote,
origin_thresh_as_vote,
..
} = self;
assert!(T::SortedListProvider::is_bag_head(&dest_stash1, *dest_thresh_as_vote, true));
assert!(T::SortedListProvider::is_bag_head(&origin_stash2, *origin_thresh_as_vote, true));
}
}

Expand Down Expand Up @@ -262,13 +310,15 @@ benchmarks! {
let original_bonded: BalanceOf<T> = ledger.active;
whitelist_account!(stash);

scenario.assert_preconditions();
scenario.check_preconditions();

}: _(RawOrigin::Signed(stash.clone()), max_additional)
verify {
let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created after")?;
let new_bonded: BalanceOf<T> = ledger.active;
assert!(original_bonded < new_bonded);

scenario.check_postconditions();
}

unbond {
Expand All @@ -293,14 +343,16 @@ benchmarks! {
let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created before")?;
let original_bonded: BalanceOf<T> = ledger.active;

scenario.assert_preconditions();
scenario.check_preconditions();

whitelist_account!(controller);
}: _(RawOrigin::Signed(controller.clone()), amount)
verify {
let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created after")?;
let new_bonded: BalanceOf<T> = ledger.active;
assert!(original_bonded > new_bonded);

scenario.check_postconditions();
}

// Withdraw only updates the ledger
Expand Down Expand Up @@ -681,11 +733,7 @@ benchmarks! {
Ledger::<T>::insert(controller.clone(), staking_ledger.clone());
let original_bonded: BalanceOf<T> = staking_ledger.active;

scenario.assert_preconditions();
assert_eq!(
T::SortedListProvider::iter().collect::<Vec<_>>(),
vec![scenario.dest_stash1.clone(), stash.clone(), scenario.origin_stash2.clone()]
);
scenario.check_preconditions();

whitelist_account!(controller);
}: _(RawOrigin::Signed(controller.clone()), rebond_amount)
Expand All @@ -694,11 +742,7 @@ benchmarks! {
let new_bonded: BalanceOf<T> = ledger.active;
assert!(original_bonded < new_bonded);

// the ordering in the list doesn't change
assert_eq!(
T::SortedListProvider::iter().collect::<Vec<_>>(),
vec![scenario.dest_stash1.clone(), stash.clone(), scenario.origin_stash2.clone()]
);
scenario.check_postconditions();
}

set_history_depth {
Expand Down
3 changes: 2 additions & 1 deletion frame/staking/src/pallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -768,13 +768,14 @@ pub mod pallet {
Error::<T>::InsufficientBond
);

// ledger must be updated prior to call to `Self::weight_of`.
Self::update_ledger(&controller, &ledger);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kianenigma this is an important correction, since the ledger must be updated before anything that uses weight_of (like on_update here)

// update this staker in the sorted list, if they exist in it.
if T::SortedListProvider::contains(&stash) {
T::SortedListProvider::on_update(&stash, Self::weight_of(&ledger.stash));
}

Self::deposit_event(Event::<T>::Bonded(stash.clone(), extra));
Self::update_ledger(&controller, &ledger);
}
Ok(())
}
Expand Down