Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Force bag changes in relevant benchmarks (targets #9507) #9529

Merged
merged 46 commits into from
Aug 28, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
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
168 changes: 157 additions & 11 deletions frame/staking/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ use super::*;
use crate::Pallet as Staking;
use testing_utils::*;

use frame_election_provider_support::{SortedListProvider, VoteWeight};
use frame_support::{
pallet_prelude::*,
traits::{Currency, Get, Imbalance},
traits::{Currency, CurrencyToVote, Get, Imbalance},
};
use sp_runtime::{
traits::{StaticLookup, Zero},
Expand Down Expand Up @@ -131,6 +132,78 @@ pub fn create_validator_with_nominators<T: Config>(
Ok((v_stash, nominators))
}

struct RebagScenario<T: Config> {
Copy link
Contributor

Choose a reason for hiding this comment

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

My fundamental issue with this start the moment I see the word bag in pallet-staking. Further down I also see notions of threshold which is again OT in staking.

In an ideal world and in my head, staking should only work and talk to SortedListProvider. I might be oversimplifying it, but I think the majority of the staking benchmark for something like bond_extra should stay the same, and it should only call T::SortedListProvider::prepare_worse_case(who, amount), which means "I need you to make sure a rebag (worse case) happens if I change backing of who by amount".

Then, in the receiving side, we make sure that this rebag happens.

Thinking a bit more, it might be easier to do this:

  • Staking will call T::SortedListProvider::weight_update_worse_case(who), in which bags-list will tell staking by how much it should increase who's stake to cause a worse case (aka. rebag).

And a similar analogy should exist in the rest of them as well.


That being said, the benchmarks seem to be correct and solid in terms of delivering worse-case code execution, so I think doing this refactor in a follow-up is also acceptable.

dest_stash1: T::AccountId,
/// Stash that is expected to be rebagged.
origin_stash1: T::AccountId,
/// Controller of the Stash that is expected to be rebagged.
origin_controller1: T::AccountId,
origin_stash2: T::AccountId,
dest_bag_thresh: BalanceOf<T>,
origin_bag_thresh: BalanceOf<T>,
}

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 the 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(
origin_bag_thresh: BalanceOf<T>,
dest_bag_thresh: BalanceOf<T>,
) -> Result<Self, &'static str> {
let total_issuance = T::Currency::total_issuance();
ensure!(
!origin_bag_thresh.is_zero() && !dest_bag_thresh.is_zero(),
"both thresholds must be greater than 0"
);

// create_stash_controller takes a factor, so we compute it.
let origin_factor: BalanceOf<T> =
(origin_bag_thresh * 10u32.into() / T::Currency::minimum_balance());
let dest_factor: BalanceOf<T> =
(dest_bag_thresh * 10u32.into() / T::Currency::minimum_balance());

// create a validator to nominate
let validator = create_validators::<T>(1, 100).unwrap().first().unwrap().clone();

// create an account in the destination bag
let (dest_stash1, dest_controller1) =
create_stash_controller_b::<T>(USER_SEED + 1, dest_factor, Default::default())?;
Staking::<T>::nominate(
RawOrigin::Signed(dest_controller1).into(),
vec![validator.clone()],
)?;

// create accounts in origin bag
let (origin_stash1, origin_controller1) =
create_stash_controller_b::<T>(USER_SEED + 2, origin_factor, Default::default())?;
Staking::<T>::nominate(
RawOrigin::Signed(origin_controller1.clone()).into(),
vec![validator.clone()],
)?;

let (origin_stash2, origin_controller2) =
create_stash_controller_b::<T>(USER_SEED + 3, origin_factor, Default::default())?;
Staking::<T>::nominate(
RawOrigin::Signed(origin_controller2.clone()).into(),
vec![validator.clone()],
)?;

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

const USER_SEED: u32 = 999666;

benchmarks! {
Expand All @@ -148,21 +221,56 @@ benchmarks! {
}

bond_extra {
let (stash, controller) = create_stash_controller::<T>(USER_SEED, 100, Default::default())?;
let max_additional = T::Currency::minimum_balance() * 10u32.into();
let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created before")?;
// Clean up any existing state.
clear_validators_and_nominators::<T>();

// The worst case scenario includes the voter changing bags.

let total_issuance = T::Currency::total_issuance();
// the bag the voter will start at
let origin_bag_thresh =
T::CurrencyToVote::to_currency(1u128, total_issuance);
// the bag we will move the voter to
let dest_bag_thresh =
T::CurrencyToVote::to_currency(VoteWeight::MAX as u128, total_issuance);

let scenario
= RebagScenario::<T>::new(origin_bag_thresh, dest_bag_thresh)?;

let max_additional = dest_bag_thresh - origin_bag_thresh;

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

}: _(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);
}

unbond {
let (_, controller) = create_stash_controller::<T>(USER_SEED, 100, Default::default())?;
let amount = T::Currency::minimum_balance() * 10u32.into();
// Clean up any existing state.
clear_validators_and_nominators::<T>();

// The worst case scenario includes the voter changing bags.

let total_issuance = T::Currency::total_issuance();
// the bag the voter will start at
let origin_bag_thresh =
T::CurrencyToVote::to_currency(VoteWeight::MAX as u128, total_issuance);
// the bag we will move the voter to
let dest_bag_thresh =
T::CurrencyToVote::to_currency(1u128, total_issuance);
let scenario
= RebagScenario::<T>::new(origin_bag_thresh, dest_bag_thresh)?;

let stash = scenario.origin_stash1.clone();
let controller = scenario.origin_controller1.clone();
let amount = origin_bag_thresh - dest_bag_thresh;
let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created before")?;
let original_bonded: BalanceOf<T> = ledger.active;
whitelist_account!(controller);
Expand Down Expand Up @@ -440,23 +548,61 @@ benchmarks! {

rebond {
let l in 1 .. MAX_UNLOCKING_CHUNKS as u32;
let (_, controller) = create_stash_controller::<T>(USER_SEED, 100, Default::default())?;
let mut staking_ledger = Ledger::<T>::get(controller.clone()).unwrap();

// Clean up any existing state.
clear_validators_and_nominators::<T>();

// The worst case scenario includes the voter changing bags.

let total_issuance = T::Currency::total_issuance();
// the bag the voter will start at
let origin_bag_thresh =
T::CurrencyToVote::to_currency(1u128, total_issuance);
// the bag we will move the voter to
let dest_bag_thresh =
T::CurrencyToVote::to_currency(VoteWeight::MAX as u128, total_issuance);
let scenario
= RebagScenario::<T>::new(origin_bag_thresh, dest_bag_thresh)?;

// rebond an amount that will put the user into the destination bag
let rebond_amount = dest_bag_thresh - origin_bag_thresh;

// spread that amount to rebond across `l` unlocking chunks,
let value = rebond_amount / l.into();
// so the sum of unlocking chunks puts voter into the dest bag
assert!(value * l.into() + origin_bag_thresh > origin_bag_thresh);
assert!(value * l.into() + origin_bag_thresh <= dest_bag_thresh);
let unlock_chunk = UnlockChunk::<BalanceOf<T>> {
value: 1u32.into(),
value,
era: EraIndex::zero(),
};

let stash = scenario.origin_stash1.clone();
let controller = scenario.origin_controller1.clone();
let mut staking_ledger = Ledger::<T>::get(controller.clone()).unwrap();

for _ in 0 .. l {
staking_ledger.unlocking.push(unlock_chunk.clone())
}
Ledger::<T>::insert(controller.clone(), staking_ledger.clone());
let original_bonded: BalanceOf<T> = staking_ledger.active;
whitelist_account!(controller);
}: _(RawOrigin::Signed(controller.clone()), (l + 100).into())

assert_eq!(
T::SortedListProvider::iter().collect::<Vec<_>>(),
vec![scenario.dest_stash1.clone(), stash.clone(), scenario.origin_stash2.clone()]
);
}: _(RawOrigin::Signed(controller.clone()), rebond_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);

// 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()]
);
}

set_history_depth {
Expand Down
36 changes: 36 additions & 0 deletions frame/staking/src/testing_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,22 @@ pub fn create_funded_user<T: Config>(
user
}

/// Grab a funded user.
pub fn create_funded_user_b<T: Config>(
string: &'static str,
n: u32,
balance_factor: crate::BalanceOf<T>,
) -> T::AccountId {
use sp_runtime::traits::Bounded;
let user = account(string, n, SEED);
let balance =
(T::Currency::minimum_balance() * balance_factor).max(BalanceOf::<T>::max_value());
T::Currency::make_free_balance_be(&user, balance);
// ensure T::CurrencyToVote will work correctly.
T::Currency::issue(balance); // TODO I don't get this .. will drop NegativeImbalance which cancels itself out
Copy link
Contributor Author

Choose a reason for hiding this comment

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

note

Copy link
Contributor

Choose a reason for hiding this comment

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

does everything work as expected without it?

user
}

/// Create a stash and controller pair.
pub fn create_stash_controller<T: Config>(
n: u32,
Expand All @@ -79,6 +95,26 @@ pub fn create_stash_controller<T: Config>(
return Ok((stash, controller))
}

/// Create a stash and controller pair.
pub fn create_stash_controller_b<T: Config>(
n: u32,
balance_factor: crate::BalanceOf<T>,
destination: RewardDestination<T::AccountId>,
) -> Result<(T::AccountId, T::AccountId), &'static str> {
let stash = create_funded_user_b::<T>("stash", n, balance_factor);
let controller = create_funded_user_b::<T>("controller", n, balance_factor);
let controller_lookup: <T::Lookup as StaticLookup>::Source =
T::Lookup::unlookup(controller.clone());
let amount = T::Currency::minimum_balance() * (balance_factor / 10u32.into()).max(1u32.into());
Staking::<T>::bond(
RawOrigin::Signed(stash.clone()).into(),
controller_lookup,
amount,
destination,
)?;
Ok((stash, controller))
}

/// Create a stash and controller pair, where the controller is dead, and payouts go to controller.
/// This is used to test worst case payout scenarios.
pub fn create_stash_and_dead_controller<T: Config>(
Expand Down