mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 16:31:07 +00:00
Update Staking Weights (#5964)
This commit is contained in:
@@ -349,6 +349,9 @@ impl<T: Trait> Module<T> {
|
||||
// -------------- IMPORTANT NOTE --------------
|
||||
// This implementation is linked to how [`should_epoch_change`] is working. This might need to
|
||||
// be updated accordingly, if the underlying mechanics of slot and epochs change.
|
||||
//
|
||||
// WEIGHT NOTE: This function is tied to the weight of `EstimateNextSessionRotation`. If you update
|
||||
// this function, you must also update the corresponding weight.
|
||||
pub fn next_expected_epoch_change(now: T::BlockNumber) -> Option<T::BlockNumber> {
|
||||
let next_slot = Self::current_epoch_start().saturating_add(T::EpochDuration::get());
|
||||
next_slot
|
||||
@@ -550,6 +553,12 @@ impl<T: Trait> frame_support::traits::EstimateNextSessionRotation<T::BlockNumber
|
||||
fn estimate_next_session_rotation(now: T::BlockNumber) -> Option<T::BlockNumber> {
|
||||
Self::next_expected_epoch_change(now)
|
||||
}
|
||||
|
||||
// The validity of this weight depends on the implementation of `estimate_next_session_rotation`
|
||||
fn weight(_now: T::BlockNumber) -> Weight {
|
||||
// Read: Current Slot, Epoch Index, Genesis Slot
|
||||
T::DbWeight::get().reads(3)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> frame_support::traits::Lateness<T::BlockNumber> for Module<T> {
|
||||
|
||||
@@ -170,6 +170,14 @@ impl<
|
||||
offset
|
||||
})
|
||||
}
|
||||
|
||||
fn weight(_now: BlockNumber) -> Weight {
|
||||
// Weight note: `estimate_next_session_rotation` has no storage reads and trivial computational overhead.
|
||||
// There should be no risk to the chain having this weight value be zero for now.
|
||||
// However, this value of zero was not properly calculated, and so it would be reasonable
|
||||
// to come back here and properly calculate the weight of this function.
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for managing creation of new validator set.
|
||||
@@ -785,4 +793,8 @@ impl<T: Trait> EstimateNextNewSession<T::BlockNumber> for Module<T> {
|
||||
fn estimate_next_new_session(now: T::BlockNumber) -> Option<T::BlockNumber> {
|
||||
T::NextSessionRotation::estimate_next_session_rotation(now)
|
||||
}
|
||||
|
||||
fn weight(now: T::BlockNumber) -> Weight {
|
||||
T::NextSessionRotation::weight(now)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,11 @@ use frame_system::RawOrigin;
|
||||
use frame_benchmarking::{benchmarks, account};
|
||||
|
||||
use crate::Module as Staking;
|
||||
use frame_system::Module as System;
|
||||
|
||||
const SEED: u32 = 0;
|
||||
const MAX_SPANS: u32 = 100;
|
||||
const MAX_VALIDATORS: u32 = 1000;
|
||||
const MAX_SLASHES: u32 = 1000;
|
||||
|
||||
fn create_funded_user<T: Trait>(string: &'static str, n: u32) -> T::AccountId {
|
||||
let user = account(string, n, SEED);
|
||||
@@ -62,6 +64,22 @@ fn create_validators<T: Trait>(max: u32) -> Result<Vec<<T::Lookup as StaticLooku
|
||||
Ok(validators)
|
||||
}
|
||||
|
||||
// Add slashing spans to a user account. Not relevant for actual use, only to benchmark
|
||||
// read and write operations.
|
||||
fn add_slashing_spans<T: Trait>(who: &T::AccountId, spans: u32) {
|
||||
if spans == 0 { return }
|
||||
|
||||
// For the first slashing span, we initialize
|
||||
let mut slashing_spans = crate::slashing::SlashingSpans::new(0);
|
||||
SpanSlash::<T>::insert((who, 0), crate::slashing::SpanRecord::default());
|
||||
|
||||
for i in 1 .. spans {
|
||||
assert!(slashing_spans.end_span(i));
|
||||
SpanSlash::<T>::insert((who, i), crate::slashing::SpanRecord::default());
|
||||
}
|
||||
SlashingSpans::<T>::insert(who, slashing_spans);
|
||||
}
|
||||
|
||||
// This function generates v validators and n nominators who are randomly nominating up to MAX_NOMINATIONS.
|
||||
pub fn create_validators_with_nominators_for_era<T: Trait>(v: u32, n: u32) -> Result<(), &'static str> {
|
||||
let mut validators: Vec<<T::Lookup as StaticLookup>::Source> = Vec::with_capacity(v as usize);
|
||||
@@ -157,46 +175,93 @@ benchmarks! {
|
||||
let u in ...;
|
||||
let stash = create_funded_user::<T>("stash",u);
|
||||
let controller = create_funded_user::<T>("controller", u);
|
||||
let controller_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(controller);
|
||||
let controller_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(controller.clone());
|
||||
let reward_destination = RewardDestination::Staked;
|
||||
let amount = T::Currency::minimum_balance() * 10.into();
|
||||
}: _(RawOrigin::Signed(stash), controller_lookup, amount, reward_destination)
|
||||
}: _(RawOrigin::Signed(stash.clone()), controller_lookup, amount, reward_destination)
|
||||
verify {
|
||||
assert!(Bonded::<T>::contains_key(stash));
|
||||
assert!(Ledger::<T>::contains_key(controller));
|
||||
}
|
||||
|
||||
bond_extra {
|
||||
let u in ...;
|
||||
let (stash, _) = create_stash_controller::<T>(u)?;
|
||||
let (stash, controller) = create_stash_controller::<T>(u)?;
|
||||
let max_additional = T::Currency::minimum_balance() * 10.into();
|
||||
let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created before")?;
|
||||
let original_bonded: BalanceOf<T> = ledger.active;
|
||||
}: _(RawOrigin::Signed(stash), 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 u in ...;
|
||||
let (_, controller) = create_stash_controller::<T>(u)?;
|
||||
let amount = T::Currency::minimum_balance() * 10.into();
|
||||
}: _(RawOrigin::Signed(controller), amount)
|
||||
let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created before")?;
|
||||
let original_bonded: BalanceOf<T> = ledger.active;
|
||||
}: _(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);
|
||||
}
|
||||
|
||||
// Withdraw only updates the ledger
|
||||
withdraw_unbonded_update {
|
||||
// Slashing Spans
|
||||
let s in 0 .. MAX_SPANS;
|
||||
let (stash, controller) = create_stash_controller::<T>(0)?;
|
||||
add_slashing_spans::<T>(&stash, s);
|
||||
let amount = T::Currency::minimum_balance() * 5.into(); // Half of total
|
||||
Staking::<T>::unbond(RawOrigin::Signed(controller.clone()).into(), amount)?;
|
||||
CurrentEra::put(EraIndex::max_value());
|
||||
let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created before")?;
|
||||
let original_total: BalanceOf<T> = ledger.total;
|
||||
}: withdraw_unbonded(RawOrigin::Signed(controller.clone()), s)
|
||||
verify {
|
||||
let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created after")?;
|
||||
let new_total: BalanceOf<T> = ledger.total;
|
||||
assert!(original_total > new_total);
|
||||
}
|
||||
|
||||
// Worst case scenario, everything is removed after the bonding duration
|
||||
withdraw_unbonded {
|
||||
let u in ...;
|
||||
let (stash, controller) = create_stash_controller::<T>(u)?;
|
||||
withdraw_unbonded_kill {
|
||||
// Slashing Spans
|
||||
let s in 0 .. MAX_SPANS;
|
||||
let (stash, controller) = create_stash_controller::<T>(0)?;
|
||||
add_slashing_spans::<T>(&stash, s);
|
||||
let amount = T::Currency::minimum_balance() * 10.into();
|
||||
Staking::<T>::unbond(RawOrigin::Signed(controller.clone()).into(), amount)?;
|
||||
let current_block = System::<T>::block_number();
|
||||
// let unbond_block = current_block + T::BondingDuration::get().into() + 10.into();
|
||||
// System::<T>::set_block_number(unbond_block);
|
||||
}: _(RawOrigin::Signed(controller))
|
||||
CurrentEra::put(EraIndex::max_value());
|
||||
let ledger = Ledger::<T>::get(&controller).ok_or("ledger not created before")?;
|
||||
let original_total: BalanceOf<T> = ledger.total;
|
||||
}: withdraw_unbonded(RawOrigin::Signed(controller.clone()), s)
|
||||
verify {
|
||||
assert!(!Ledger::<T>::contains_key(controller));
|
||||
}
|
||||
|
||||
validate {
|
||||
let u in ...;
|
||||
let (_, controller) = create_stash_controller::<T>(u)?;
|
||||
let (stash, controller) = create_stash_controller::<T>(u)?;
|
||||
let prefs = ValidatorPrefs::default();
|
||||
}: _(RawOrigin::Signed(controller), prefs)
|
||||
verify {
|
||||
assert!(Validators::<T>::contains_key(stash));
|
||||
}
|
||||
|
||||
// Worst case scenario, MAX_NOMINATIONS
|
||||
nominate {
|
||||
let n in 1 .. MAX_NOMINATIONS as u32;
|
||||
let (_, controller) = create_stash_controller::<T>(n + 1)?;
|
||||
let (stash, controller) = create_stash_controller::<T>(n + 1)?;
|
||||
let validators = create_validators::<T>(n)?;
|
||||
}: _(RawOrigin::Signed(controller), validators)
|
||||
verify {
|
||||
assert!(Nominators::<T>::contains_key(stash));
|
||||
}
|
||||
|
||||
chill {
|
||||
let u in ...;
|
||||
@@ -205,61 +270,91 @@ benchmarks! {
|
||||
|
||||
set_payee {
|
||||
let u in ...;
|
||||
let (_, controller) = create_stash_controller::<T>(u)?;
|
||||
let (stash, controller) = create_stash_controller::<T>(u)?;
|
||||
assert_eq!(Payee::<T>::get(&stash), RewardDestination::Staked);
|
||||
}: _(RawOrigin::Signed(controller), RewardDestination::Controller)
|
||||
verify {
|
||||
assert_eq!(Payee::<T>::get(&stash), RewardDestination::Controller);
|
||||
}
|
||||
|
||||
set_controller {
|
||||
let u in ...;
|
||||
let (stash, _) = create_stash_controller::<T>(u)?;
|
||||
let new_controller = create_funded_user::<T>("new_controller", u);
|
||||
let new_controller_lookup = T::Lookup::unlookup(new_controller);
|
||||
let new_controller_lookup = T::Lookup::unlookup(new_controller.clone());
|
||||
}: _(RawOrigin::Signed(stash), new_controller_lookup)
|
||||
verify {
|
||||
assert!(Ledger::<T>::contains_key(&new_controller));
|
||||
}
|
||||
|
||||
set_validator_count {
|
||||
let c in 0 .. 1000;
|
||||
let c in 0 .. MAX_VALIDATORS;
|
||||
}: _(RawOrigin::Root, c)
|
||||
verify {
|
||||
assert_eq!(ValidatorCount::get(), c);
|
||||
}
|
||||
|
||||
force_no_eras { let i in 0 .. 1; }: _(RawOrigin::Root)
|
||||
verify { assert_eq!(ForceEra::get(), Forcing::ForceNone); }
|
||||
|
||||
force_new_era {let i in 0 .. 1; }: _(RawOrigin::Root)
|
||||
verify { assert_eq!(ForceEra::get(), Forcing::ForceNew); }
|
||||
|
||||
force_new_era_always { let i in 0 .. 1; }: _(RawOrigin::Root)
|
||||
verify { assert_eq!(ForceEra::get(), Forcing::ForceAlways); }
|
||||
|
||||
// Worst case scenario, the list of invulnerables is very long.
|
||||
set_invulnerables {
|
||||
let v in 0 .. 1000;
|
||||
let v in 0 .. MAX_VALIDATORS;
|
||||
let mut invulnerables = Vec::new();
|
||||
for i in 0 .. v {
|
||||
invulnerables.push(account("invulnerable", i, SEED));
|
||||
}
|
||||
}: _(RawOrigin::Root, invulnerables)
|
||||
verify {
|
||||
assert_eq!(Invulnerables::<T>::get().len(), v as usize);
|
||||
}
|
||||
|
||||
force_unstake {
|
||||
let u in ...;
|
||||
let (stash, _) = create_stash_controller::<T>(u)?;
|
||||
}: _(RawOrigin::Root, stash)
|
||||
// Slashing Spans
|
||||
let s in 0 .. MAX_SPANS;
|
||||
let (stash, controller) = create_stash_controller::<T>(0)?;
|
||||
add_slashing_spans::<T>(&stash, s);
|
||||
}: _(RawOrigin::Root, stash, s)
|
||||
verify {
|
||||
assert!(!Ledger::<T>::contains_key(&controller));
|
||||
}
|
||||
|
||||
cancel_deferred_slash {
|
||||
let s in 1 .. 1000;
|
||||
let s in 1 .. MAX_SLASHES;
|
||||
let mut unapplied_slashes = Vec::new();
|
||||
let era = EraIndex::one();
|
||||
for _ in 0 .. 1000 {
|
||||
for _ in 0 .. MAX_SLASHES {
|
||||
unapplied_slashes.push(UnappliedSlash::<T::AccountId, BalanceOf<T>>::default());
|
||||
}
|
||||
UnappliedSlashes::<T>::insert(era, &unapplied_slashes);
|
||||
|
||||
let slash_indices: Vec<u32> = (0 .. s).collect();
|
||||
}: _(RawOrigin::Root, era, slash_indices)
|
||||
verify {
|
||||
assert_eq!(UnappliedSlashes::<T>::get(&era).len(), (MAX_SLASHES - s) as usize);
|
||||
}
|
||||
|
||||
payout_stakers {
|
||||
let n in 1 .. MAX_NOMINATIONS as u32;
|
||||
let validator = create_validator_with_nominators::<T>(n, MAX_NOMINATIONS as u32)?;
|
||||
let n in 1 .. T::MaxNominatorRewardedPerValidator::get() as u32;
|
||||
let validator = create_validator_with_nominators::<T>(n, T::MaxNominatorRewardedPerValidator::get() as u32)?;
|
||||
let current_era = CurrentEra::get().unwrap();
|
||||
let caller = account("caller", n, SEED);
|
||||
}: _(RawOrigin::Signed(caller), validator, current_era)
|
||||
let caller = account("caller", 0, SEED);
|
||||
let balance_before = T::Currency::free_balance(&validator);
|
||||
}: _(RawOrigin::Signed(caller), validator.clone(), current_era)
|
||||
verify {
|
||||
// Validator has been paid!
|
||||
let balance_after = T::Currency::free_balance(&validator);
|
||||
assert!(balance_before < balance_after);
|
||||
}
|
||||
|
||||
rebond {
|
||||
let l in 1 .. 1000;
|
||||
let l in 1 .. MAX_UNLOCKING_CHUNKS as u32;
|
||||
let (_, controller) = create_stash_controller::<T>(u)?;
|
||||
let mut staking_ledger = Ledger::<T>::get(controller.clone()).unwrap();
|
||||
let unlock_chunk = UnlockChunk::<BalanceOf<T>> {
|
||||
@@ -269,8 +364,14 @@ benchmarks! {
|
||||
for _ in 0 .. l {
|
||||
staking_ledger.unlocking.push(unlock_chunk.clone())
|
||||
}
|
||||
Ledger::<T>::insert(controller.clone(), staking_ledger);
|
||||
}: _(RawOrigin::Signed(controller), (l + 100).into())
|
||||
Ledger::<T>::insert(controller.clone(), staking_ledger.clone());
|
||||
let original_bonded: BalanceOf<T> = staking_ledger.active;
|
||||
}: _(RawOrigin::Signed(controller.clone()), (l + 100).into())
|
||||
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);
|
||||
}
|
||||
|
||||
set_history_depth {
|
||||
let e in 1 .. 100;
|
||||
@@ -285,13 +386,20 @@ benchmarks! {
|
||||
<ErasTotalStake<T>>::insert(i, BalanceOf::<T>::one());
|
||||
ErasStartSessionIndex::insert(i, i);
|
||||
}
|
||||
}: _(RawOrigin::Root, EraIndex::zero())
|
||||
}: _(RawOrigin::Root, EraIndex::zero(), u32::max_value())
|
||||
verify {
|
||||
assert_eq!(HistoryDepth::get(), 0);
|
||||
}
|
||||
|
||||
reap_stash {
|
||||
let u in 1 .. 1000;
|
||||
let (stash, controller) = create_stash_controller::<T>(u)?;
|
||||
let s in 1 .. MAX_SPANS;
|
||||
let (stash, controller) = create_stash_controller::<T>(0)?;
|
||||
add_slashing_spans::<T>(&stash, s);
|
||||
T::Currency::make_free_balance_be(&stash, 0.into());
|
||||
}: _(RawOrigin::Signed(controller), stash)
|
||||
}: _(RawOrigin::Signed(controller), stash.clone(), s)
|
||||
verify {
|
||||
assert!(!Bonded::<T>::contains_key(&stash));
|
||||
}
|
||||
|
||||
new_era {
|
||||
let v in 1 .. 10;
|
||||
@@ -305,7 +413,7 @@ benchmarks! {
|
||||
}
|
||||
|
||||
do_slash {
|
||||
let l in 1 .. 1000;
|
||||
let l in 1 .. MAX_UNLOCKING_CHUNKS as u32;
|
||||
let (stash, controller) = create_stash_controller::<T>(0)?;
|
||||
let mut staking_ledger = Ledger::<T>::get(controller.clone()).unwrap();
|
||||
let unlock_chunk = UnlockChunk::<BalanceOf<T>> {
|
||||
@@ -317,6 +425,7 @@ benchmarks! {
|
||||
}
|
||||
Ledger::<T>::insert(controller.clone(), staking_ledger.clone());
|
||||
let slash_amount = T::Currency::minimum_balance() * 10.into();
|
||||
let balance_before = T::Currency::free_balance(&stash);
|
||||
}: {
|
||||
crate::slashing::do_slash::<T>(
|
||||
&stash,
|
||||
@@ -324,6 +433,9 @@ benchmarks! {
|
||||
&mut BalanceOf::<T>::zero(),
|
||||
&mut NegativeImbalanceOf::<T>::zero()
|
||||
);
|
||||
} verify {
|
||||
let balance_after = T::Currency::free_balance(&stash);
|
||||
assert!(balance_before > balance_after);
|
||||
}
|
||||
|
||||
payout_all {
|
||||
@@ -395,7 +507,7 @@ mod tests {
|
||||
|
||||
let validator_stash = create_validator_with_nominators::<Test>(
|
||||
n,
|
||||
MAX_NOMINATIONS as u32,
|
||||
<Test as Trait>::MaxNominatorRewardedPerValidator::get() as u32,
|
||||
).unwrap();
|
||||
|
||||
let current_era = CurrentEra::get().unwrap();
|
||||
@@ -408,6 +520,35 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_slashing_spans_works() {
|
||||
ExtBuilder::default().has_stakers(false).build().execute_with(|| {
|
||||
let n = 10;
|
||||
|
||||
let validator_stash = create_validator_with_nominators::<Test>(
|
||||
n,
|
||||
<Test as Trait>::MaxNominatorRewardedPerValidator::get() as u32,
|
||||
).unwrap();
|
||||
|
||||
// Add 20 slashing spans
|
||||
let num_of_slashing_spans = 20;
|
||||
add_slashing_spans::<Test>(&validator_stash, num_of_slashing_spans);
|
||||
|
||||
let slashing_spans = SlashingSpans::<Test>::get(&validator_stash).unwrap();
|
||||
assert_eq!(slashing_spans.iter().count(), num_of_slashing_spans as usize);
|
||||
for i in 0 .. num_of_slashing_spans {
|
||||
assert!(SpanSlash::<Test>::contains_key((&validator_stash, i)));
|
||||
}
|
||||
|
||||
// Test everything is cleaned up
|
||||
assert_ok!(Staking::kill_stash(&validator_stash, num_of_slashing_spans));
|
||||
assert!(SlashingSpans::<Test>::get(&validator_stash).is_none());
|
||||
for i in 0 .. num_of_slashing_spans {
|
||||
assert!(!SpanSlash::<Test>::contains_key((&validator_stash, i)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_payout_all() {
|
||||
ExtBuilder::default().has_stakers(false).build().execute_with(|| {
|
||||
@@ -432,7 +573,8 @@ mod tests {
|
||||
assert_ok!(test_benchmark_bond::<Test>());
|
||||
assert_ok!(test_benchmark_bond_extra::<Test>());
|
||||
assert_ok!(test_benchmark_unbond::<Test>());
|
||||
assert_ok!(test_benchmark_withdraw_unbonded::<Test>());
|
||||
assert_ok!(test_benchmark_withdraw_unbonded_update::<Test>());
|
||||
assert_ok!(test_benchmark_withdraw_unbonded_kill::<Test>());
|
||||
assert_ok!(test_benchmark_validate::<Test>());
|
||||
assert_ok!(test_benchmark_nominate::<Test>());
|
||||
assert_ok!(test_benchmark_chill::<Test>());
|
||||
|
||||
@@ -290,9 +290,9 @@ use sp_std::{
|
||||
use codec::{HasCompact, Encode, Decode};
|
||||
use frame_support::{
|
||||
decl_module, decl_event, decl_storage, ensure, decl_error, debug,
|
||||
weights::{Weight, DispatchClass},
|
||||
weights::{Weight, constants::{WEIGHT_PER_MICROS, WEIGHT_PER_NANOS}},
|
||||
storage::IterableStorageMap,
|
||||
dispatch::{IsSubType, DispatchResult},
|
||||
dispatch::{IsSubType, DispatchResult, DispatchResultWithPostInfo},
|
||||
traits::{
|
||||
Currency, LockIdentifier, LockableCurrency, WithdrawReasons, OnUnbalanced, Imbalance, Get,
|
||||
UnixTime, EstimateNextNewSession, EnsureOrigin,
|
||||
@@ -1164,6 +1164,10 @@ decl_error! {
|
||||
PhragmenBogusScore,
|
||||
/// The call is not allowed at the given time due to restrictions of election period.
|
||||
CallNotAllowed,
|
||||
/// Incorrect previous history depth input provided.
|
||||
IncorrectHistoryDepth,
|
||||
/// Incorrect number of slashing spans provided.
|
||||
IncorrectSlashingSpans,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1185,6 +1189,11 @@ decl_module! {
|
||||
/// worker, if applicable, will execute at the end of the current block, and solutions may
|
||||
/// be submitted.
|
||||
fn on_initialize(now: T::BlockNumber) -> Weight {
|
||||
let mut consumed_weight = 0;
|
||||
let mut add_weight = |reads, writes, weight| {
|
||||
consumed_weight += T::DbWeight::get().reads_writes(reads, writes);
|
||||
consumed_weight += weight;
|
||||
};
|
||||
if
|
||||
// if we don't have any ongoing offchain compute.
|
||||
Self::era_election_status().is_closed() &&
|
||||
@@ -1195,12 +1204,15 @@ decl_module! {
|
||||
if let Some(remaining) = next_session_change.checked_sub(&now) {
|
||||
if remaining <= T::ElectionLookahead::get() && !remaining.is_zero() {
|
||||
// create snapshot.
|
||||
if Self::create_stakers_snapshot() {
|
||||
let (did_snapshot, snapshot_weight) = Self::create_stakers_snapshot();
|
||||
add_weight(0, 0, snapshot_weight);
|
||||
if did_snapshot {
|
||||
// Set the flag to make sure we don't waste any compute here in the same era
|
||||
// after we have triggered the offline compute.
|
||||
<EraElectionStatus<T>>::put(
|
||||
ElectionStatus::<T::BlockNumber>::Open(now)
|
||||
);
|
||||
add_weight(0, 1, 0);
|
||||
log!(info, "💸 Election window is Open({:?}). Snapshot created", now);
|
||||
} else {
|
||||
log!(warn, "💸 Failed to create snapshot at {:?}.", now);
|
||||
@@ -1210,10 +1222,13 @@ decl_module! {
|
||||
} else {
|
||||
log!(warn, "💸 Estimating next session change failed.");
|
||||
}
|
||||
add_weight(0, 0, T::NextNewSession::weight(now))
|
||||
}
|
||||
|
||||
// weight
|
||||
50_000
|
||||
// For `era_election_status`, `is_current_session_final`, `will_era_be_forced`
|
||||
add_weight(3, 0, 0);
|
||||
// Additional read from `on_finalize`
|
||||
add_weight(1, 0, 0);
|
||||
consumed_weight
|
||||
}
|
||||
|
||||
/// Check if the current block number is the one at which the election window has been set
|
||||
@@ -1241,9 +1256,11 @@ decl_module! {
|
||||
if active_era.start.is_none() {
|
||||
let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::<u64>();
|
||||
active_era.start = Some(now_as_millis_u64);
|
||||
// This write only ever happens once, we don't include it in the weight in general
|
||||
ActiveEra::put(active_era);
|
||||
}
|
||||
}
|
||||
// `on_finalize` weight is tracked in `on_initialize`
|
||||
}
|
||||
|
||||
/// Take the origin account as a stash and lock up `value` of its balance. `controller` will
|
||||
@@ -1262,8 +1279,13 @@ decl_module! {
|
||||
///
|
||||
/// NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are _never_ cleaned
|
||||
/// unless the `origin` falls below _existential deposit_ and gets removed as dust.
|
||||
/// ------------------
|
||||
/// Base Weight: 67.87 µs
|
||||
/// DB Weight:
|
||||
/// - Read: Bonded, Ledger, [Origin Account], Current Era, History Depth, Locks
|
||||
/// - Write: Bonded, Payee, [Origin Account], Locks, Ledger
|
||||
/// # </weight>
|
||||
#[weight = 500_000_000]
|
||||
#[weight = 67 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(5, 4)]
|
||||
pub fn bond(origin,
|
||||
controller: <T::Lookup as StaticLookup>::Source,
|
||||
#[compact] value: BalanceOf<T>,
|
||||
@@ -1326,8 +1348,13 @@ decl_module! {
|
||||
/// - Independent of the arguments. Insignificant complexity.
|
||||
/// - O(1).
|
||||
/// - One DB entry.
|
||||
/// ------------
|
||||
/// Base Weight: 54.88 µs
|
||||
/// DB Weight:
|
||||
/// - Read: Era Election Status, Bonded, Ledger, [Origin Account], Locks
|
||||
/// - Write: [Origin Account], Locks, Ledger
|
||||
/// # </weight>
|
||||
#[weight = 500_000_000]
|
||||
#[weight = 55 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(4, 2)]
|
||||
fn bond_extra(origin, #[compact] max_additional: BalanceOf<T>) {
|
||||
ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed);
|
||||
let stash = ensure_signed(origin)?;
|
||||
@@ -1372,8 +1399,13 @@ decl_module! {
|
||||
/// The only way to clean the aforementioned storage item is also user-controlled via
|
||||
/// `withdraw_unbonded`.
|
||||
/// - One DB entry.
|
||||
/// ----------
|
||||
/// Base Weight: 50.34 µs
|
||||
/// DB Weight:
|
||||
/// - Read: Era Election Status, Ledger, Current Era, Locks, [Origin Account]
|
||||
/// - Write: [Origin Account], Locks, Ledger
|
||||
/// </weight>
|
||||
#[weight = 400_000_000]
|
||||
#[weight = 50 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(4, 2)]
|
||||
fn unbond(origin, #[compact] value: BalanceOf<T>) {
|
||||
ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed);
|
||||
let controller = ensure_signed(origin)?;
|
||||
@@ -1420,9 +1452,28 @@ decl_module! {
|
||||
/// indirectly user-controlled. See [`unbond`] for more detail.
|
||||
/// - Contains a limited number of reads, yet the size of which could be large based on `ledger`.
|
||||
/// - Writes are limited to the `origin` account key.
|
||||
/// ---------------
|
||||
/// Complexity O(S) where S is the number of slashing spans to remove
|
||||
/// Base Weight:
|
||||
/// Update: 50.52 + .028 * S µs
|
||||
/// - Reads: EraElectionStatus, Ledger, Current Era, Locks, [Origin Account]
|
||||
/// - Writes: [Origin Account], Locks, Ledger
|
||||
/// Kill: 79.41 + 2.366 * S µs
|
||||
/// - Reads: EraElectionStatus, Ledger, Current Era, Bonded, Slashing Spans, [Origin Account], Locks
|
||||
/// - Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators, [Origin Account], Locks
|
||||
/// - Writes Each: SpanSlash * S
|
||||
/// NOTE: Weight annotation is the kill scenario, we refund otherwise.
|
||||
/// # </weight>
|
||||
#[weight = 400_000_000]
|
||||
fn withdraw_unbonded(origin) {
|
||||
#[weight = T::DbWeight::get().reads_writes(6, 6)
|
||||
.saturating_add(80 * WEIGHT_PER_MICROS)
|
||||
.saturating_add(
|
||||
(2 * WEIGHT_PER_MICROS).saturating_mul(Weight::from(*num_slashing_spans))
|
||||
)
|
||||
.saturating_add(T::DbWeight::get().writes(Weight::from(*num_slashing_spans)))
|
||||
// if slashing spans is non-zero, add 1 more write
|
||||
.saturating_add(T::DbWeight::get().writes(Weight::from(*num_slashing_spans).min(1)))
|
||||
]
|
||||
fn withdraw_unbonded(origin, num_slashing_spans: u32) -> DispatchResultWithPostInfo {
|
||||
ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed);
|
||||
let controller = ensure_signed(origin)?;
|
||||
let mut ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?;
|
||||
@@ -1431,17 +1482,21 @@ decl_module! {
|
||||
ledger = ledger.consolidate_unlocked(current_era)
|
||||
}
|
||||
|
||||
if ledger.unlocking.is_empty() && ledger.active.is_zero() {
|
||||
let post_info_weight = if ledger.unlocking.is_empty() && ledger.active.is_zero() {
|
||||
// This account must have called `unbond()` with some value that caused the active
|
||||
// portion to fall below existential deposit + will have no more unlocking chunks
|
||||
// left. We can now safely remove all staking-related information.
|
||||
Self::kill_stash(&stash)?;
|
||||
Self::kill_stash(&stash, num_slashing_spans)?;
|
||||
// remove the lock.
|
||||
T::Currency::remove_lock(STAKING_ID, &stash);
|
||||
// This is worst case scenario, so we use the full weight and return None
|
||||
None
|
||||
} else {
|
||||
// This was the consequence of a partial unbond. just update the ledger and move on.
|
||||
Self::update_ledger(&controller, &ledger);
|
||||
}
|
||||
// This is only an update, so we use less overall weight
|
||||
Some(50 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(4, 2))
|
||||
};
|
||||
|
||||
// `old_total` should never be less than the new total because
|
||||
// `consolidate_unlocked` strictly subtracts balance.
|
||||
@@ -1450,6 +1505,8 @@ decl_module! {
|
||||
let value = old_total - ledger.total;
|
||||
Self::deposit_event(RawEvent::Withdrawn(stash, value));
|
||||
}
|
||||
|
||||
Ok(post_info_weight.into())
|
||||
}
|
||||
|
||||
/// Declare the desire to validate for the origin controller.
|
||||
@@ -1463,8 +1520,13 @@ decl_module! {
|
||||
/// - Independent of the arguments. Insignificant complexity.
|
||||
/// - Contains a limited number of reads.
|
||||
/// - Writes are limited to the `origin` account key.
|
||||
/// -----------
|
||||
/// Base Weight: 17.13 µs
|
||||
/// DB Weight:
|
||||
/// - Read: Era Election Status, Ledger
|
||||
/// - Write: Nominators, Validators
|
||||
/// # </weight>
|
||||
#[weight = 750_000_000]
|
||||
#[weight = 17 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(2, 2)]
|
||||
pub fn validate(origin, prefs: ValidatorPrefs) {
|
||||
ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed);
|
||||
let controller = ensure_signed(origin)?;
|
||||
@@ -1483,11 +1545,20 @@ decl_module! {
|
||||
/// And, it can be only called when [`EraElectionStatus`] is `Closed`.
|
||||
///
|
||||
/// # <weight>
|
||||
/// - The transaction's complexity is proportional to the size of `targets`,
|
||||
/// which is capped at CompactAssignments::LIMIT.
|
||||
/// - The transaction's complexity is proportional to the size of `targets` (N)
|
||||
/// which is capped at CompactAssignments::LIMIT (MAX_NOMINATIONS).
|
||||
/// - Both the reads and writes follow a similar pattern.
|
||||
/// ---------
|
||||
/// Base Weight: 22.34 + .36 * N µs
|
||||
/// where N is the number of targets
|
||||
/// DB Weight:
|
||||
/// - Reads: Era Election Status, Ledger, Current Era
|
||||
/// - Writes: Validators, Nominators
|
||||
/// # </weight>
|
||||
#[weight = 750_000_000]
|
||||
#[weight = T::DbWeight::get().reads_writes(3, 2)
|
||||
.saturating_add(22 * WEIGHT_PER_MICROS)
|
||||
.saturating_add((360 * WEIGHT_PER_NANOS).saturating_mul(targets.len() as Weight))
|
||||
]
|
||||
pub fn nominate(origin, targets: Vec<<T::Lookup as StaticLookup>::Source>) {
|
||||
ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed);
|
||||
let controller = ensure_signed(origin)?;
|
||||
@@ -1495,7 +1566,7 @@ decl_module! {
|
||||
let stash = &ledger.stash;
|
||||
ensure!(!targets.is_empty(), Error::<T>::EmptyTargets);
|
||||
let targets = targets.into_iter()
|
||||
.take(<CompactAssignments as VotingLimit>::LIMIT)
|
||||
.take(MAX_NOMINATIONS)
|
||||
.map(|t| T::Lookup::lookup(t))
|
||||
.collect::<result::Result<Vec<T::AccountId>, _>>()?;
|
||||
|
||||
@@ -1521,8 +1592,13 @@ decl_module! {
|
||||
/// - Independent of the arguments. Insignificant complexity.
|
||||
/// - Contains one read.
|
||||
/// - Writes are limited to the `origin` account key.
|
||||
/// --------
|
||||
/// Base Weight: 16.53 µs
|
||||
/// DB Weight:
|
||||
/// - Read: EraElectionStatus, Ledger
|
||||
/// - Write: Validators, Nominators
|
||||
/// # </weight>
|
||||
#[weight = 500_000_000]
|
||||
#[weight = 16 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(2, 2)]
|
||||
fn chill(origin) {
|
||||
ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed);
|
||||
let controller = ensure_signed(origin)?;
|
||||
@@ -1540,8 +1616,13 @@ decl_module! {
|
||||
/// - Independent of the arguments. Insignificant complexity.
|
||||
/// - Contains a limited number of reads.
|
||||
/// - Writes are limited to the `origin` account key.
|
||||
/// ---------
|
||||
/// - Base Weight: 11.33 µs
|
||||
/// - DB Weight:
|
||||
/// - Read: Ledger
|
||||
/// - Write: Payee
|
||||
/// # </weight>
|
||||
#[weight = 500_000_000]
|
||||
#[weight = 11 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(1, 1)]
|
||||
fn set_payee(origin, payee: RewardDestination) {
|
||||
let controller = ensure_signed(origin)?;
|
||||
let ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?;
|
||||
@@ -1559,8 +1640,13 @@ decl_module! {
|
||||
/// - Independent of the arguments. Insignificant complexity.
|
||||
/// - Contains a limited number of reads.
|
||||
/// - Writes are limited to the `origin` account key.
|
||||
/// ----------
|
||||
/// Base Weight: 25.22 µs
|
||||
/// DB Weight:
|
||||
/// - Read: Bonded, Ledger New Controller, Ledger Old Controller
|
||||
/// - Write: Bonded, Ledger New Controller, Ledger Old Controller
|
||||
/// # </weight>
|
||||
#[weight = 750_000_000]
|
||||
#[weight = 25 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(3, 3)]
|
||||
fn set_controller(origin, controller: <T::Lookup as StaticLookup>::Source) {
|
||||
let stash = ensure_signed(origin)?;
|
||||
let old_controller = Self::bonded(&stash).ok_or(Error::<T>::NotStash)?;
|
||||
@@ -1576,8 +1662,15 @@ decl_module! {
|
||||
}
|
||||
}
|
||||
|
||||
/// The ideal number of validators.
|
||||
#[weight = 5_000_000]
|
||||
/// Sets the ideal number of validators.
|
||||
///
|
||||
/// The dispatch origin must be Root.
|
||||
///
|
||||
/// # <weight>
|
||||
/// Base Weight: 1.717 µs
|
||||
/// Write: Validator Count
|
||||
/// # </weight>
|
||||
#[weight = 2 * WEIGHT_PER_MICROS + T::DbWeight::get().writes(1)]
|
||||
fn set_validator_count(origin, #[compact] new: u32) {
|
||||
ensure_root(origin)?;
|
||||
ValidatorCount::put(new);
|
||||
@@ -1585,10 +1678,14 @@ decl_module! {
|
||||
|
||||
/// Force there to be no new eras indefinitely.
|
||||
///
|
||||
/// The dispatch origin must be Root.
|
||||
///
|
||||
/// # <weight>
|
||||
/// - No arguments.
|
||||
/// - Base Weight: 1.857 µs
|
||||
/// - Write: ForceEra
|
||||
/// # </weight>
|
||||
#[weight = 5_000_000]
|
||||
#[weight = 2 * WEIGHT_PER_MICROS + T::DbWeight::get().writes(1)]
|
||||
fn force_no_eras(origin) {
|
||||
ensure_root(origin)?;
|
||||
ForceEra::put(Forcing::ForceNone);
|
||||
@@ -1597,29 +1694,62 @@ decl_module! {
|
||||
/// Force there to be a new era at the end of the next session. After this, it will be
|
||||
/// reset to normal (non-forced) behaviour.
|
||||
///
|
||||
/// The dispatch origin must be Root.
|
||||
///
|
||||
/// # <weight>
|
||||
/// - No arguments.
|
||||
/// - Base Weight: 1.959 µs
|
||||
/// - Write ForceEra
|
||||
/// # </weight>
|
||||
#[weight = 5_000_000]
|
||||
#[weight = 2 * WEIGHT_PER_MICROS + T::DbWeight::get().writes(1)]
|
||||
fn force_new_era(origin) {
|
||||
ensure_root(origin)?;
|
||||
ForceEra::put(Forcing::ForceNew);
|
||||
}
|
||||
|
||||
/// Set the validators who cannot be slashed (if any).
|
||||
#[weight = 5_000_000]
|
||||
///
|
||||
/// The dispatch origin must be Root.
|
||||
///
|
||||
/// # <weight>
|
||||
/// - O(V)
|
||||
/// - Base Weight: 2.208 + .006 * V µs
|
||||
/// - Write: Invulnerables
|
||||
/// # </weight>
|
||||
#[weight = T::DbWeight::get().writes(1)
|
||||
.saturating_add(2 * WEIGHT_PER_MICROS)
|
||||
.saturating_add((6 * WEIGHT_PER_NANOS).saturating_mul(validators.len() as Weight))
|
||||
]
|
||||
fn set_invulnerables(origin, validators: Vec<T::AccountId>) {
|
||||
ensure_root(origin)?;
|
||||
<Invulnerables<T>>::put(validators);
|
||||
}
|
||||
|
||||
/// Force a current staker to become completely unstaked, immediately.
|
||||
#[weight = 0]
|
||||
fn force_unstake(origin, stash: T::AccountId) {
|
||||
///
|
||||
/// The dispatch origin must be Root.
|
||||
///
|
||||
/// # <weight>
|
||||
/// O(S) where S is the number of slashing spans to be removed
|
||||
/// Base Weight: 53.07 + 2.365 * S µs
|
||||
/// Reads: Bonded, Slashing Spans, Account, Locks
|
||||
/// Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators, Account, Locks
|
||||
/// Writes Each: SpanSlash * S
|
||||
/// # </weight>
|
||||
#[weight = T::DbWeight::get().reads_writes(4, 7)
|
||||
.saturating_add(53 * WEIGHT_PER_MICROS)
|
||||
.saturating_add(
|
||||
WEIGHT_PER_MICROS.saturating_mul(2).saturating_mul(Weight::from(*num_slashing_spans))
|
||||
)
|
||||
.saturating_add(T::DbWeight::get().writes(Weight::from(*num_slashing_spans)))
|
||||
// if slashing spans is non-zero, add 1 more write
|
||||
.saturating_add(T::DbWeight::get().writes(Weight::from(*num_slashing_spans > 0)))
|
||||
]
|
||||
fn force_unstake(origin, stash: T::AccountId, num_slashing_spans: u32) {
|
||||
ensure_root(origin)?;
|
||||
|
||||
// remove all staking-related information.
|
||||
Self::kill_stash(&stash)?;
|
||||
Self::kill_stash(&stash, num_slashing_spans)?;
|
||||
|
||||
// remove the lock.
|
||||
T::Currency::remove_lock(STAKING_ID, &stash);
|
||||
@@ -1627,23 +1757,36 @@ decl_module! {
|
||||
|
||||
/// Force there to be a new era at the end of sessions indefinitely.
|
||||
///
|
||||
/// The dispatch origin must be Root.
|
||||
///
|
||||
/// # <weight>
|
||||
/// - One storage write
|
||||
/// - Base Weight: 2.05 µs
|
||||
/// - Write: ForceEra
|
||||
/// # </weight>
|
||||
#[weight = 5_000_000]
|
||||
#[weight = 2 * WEIGHT_PER_MICROS + T::DbWeight::get().writes(1)]
|
||||
fn force_new_era_always(origin) {
|
||||
ensure_root(origin)?;
|
||||
ForceEra::put(Forcing::ForceAlways);
|
||||
}
|
||||
|
||||
/// Cancel enactment of a deferred slash. Can be called by either the root origin or
|
||||
/// the `T::SlashCancelOrigin`.
|
||||
/// passing the era and indices of the slashes for that era to kill.
|
||||
/// Cancel enactment of a deferred slash.
|
||||
///
|
||||
/// Can be called by either the root origin or the `T::SlashCancelOrigin`.
|
||||
///
|
||||
/// Parameters: era and indices of the slashes for that era to kill.
|
||||
///
|
||||
/// # <weight>
|
||||
/// - One storage write.
|
||||
/// Complexity: O(U + S)
|
||||
/// with U unapplied slashes weighted with U=1000
|
||||
/// and S is the number of slash indices to be canceled.
|
||||
/// - Base: 5870 + 34.61 * S µs
|
||||
/// - Read: Unapplied Slashes
|
||||
/// - Write: Unapplied Slashes
|
||||
/// # </weight>
|
||||
#[weight = 1_000_000_000]
|
||||
#[weight = T::DbWeight::get().reads_writes(1, 1)
|
||||
.saturating_add(5_870 * WEIGHT_PER_MICROS)
|
||||
.saturating_add((35 * WEIGHT_PER_MICROS).saturating_mul(slash_indices.len() as Weight))
|
||||
]
|
||||
fn cancel_deferred_slash(origin, era: EraIndex, slash_indices: Vec<u32>) {
|
||||
T::SlashCancelOrigin::try_origin(origin)
|
||||
.map(|_| ())
|
||||
@@ -1741,8 +1884,23 @@ decl_module! {
|
||||
/// # <weight>
|
||||
/// - Time complexity: at most O(MaxNominatorRewardedPerValidator).
|
||||
/// - Contains a limited number of reads and writes.
|
||||
/// -----------
|
||||
/// N is the Number of payouts for the validator (including the validator)
|
||||
/// Base Weight: 110 + 54.2 * N µs (Median Slopes)
|
||||
/// DB Weight:
|
||||
/// - Read: EraElectionStatus, CurrentEra, HistoryDepth, MigrateEra, ErasValidatorReward,
|
||||
/// ErasStakersClipped, ErasRewardPoints, ErasValidatorPrefs (8 items)
|
||||
/// - Read Each: Bonded, Ledger, Payee, Locks, System Account (5 items)
|
||||
/// - Write Each: System Account, Locks, Ledger (3 items)
|
||||
// TODO: Remove read on Migrate Era
|
||||
/// # </weight>
|
||||
#[weight = 500_000_000]
|
||||
#[weight =
|
||||
110 * WEIGHT_PER_MICROS
|
||||
+ 54 * WEIGHT_PER_MICROS * Weight::from(T::MaxNominatorRewardedPerValidator::get())
|
||||
+ T::DbWeight::get().reads(8)
|
||||
+ T::DbWeight::get().reads(5) * Weight::from(T::MaxNominatorRewardedPerValidator::get() + 1)
|
||||
+ T::DbWeight::get().writes(3) * Weight::from(T::MaxNominatorRewardedPerValidator::get() + 1)
|
||||
]
|
||||
fn payout_stakers(origin, validator_stash: T::AccountId, era: EraIndex) -> DispatchResult {
|
||||
ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed);
|
||||
ensure_signed(origin)?;
|
||||
@@ -1755,11 +1913,21 @@ decl_module! {
|
||||
/// [`EraElectionStatus`] is `Closed`.
|
||||
///
|
||||
/// # <weight>
|
||||
/// - Time complexity: O(1). Bounded by `MAX_UNLOCKING_CHUNKS`.
|
||||
/// - Time complexity: O(L), where L is unlocking chunks
|
||||
/// - Bounded by `MAX_UNLOCKING_CHUNKS`.
|
||||
/// - Storage changes: Can't increase storage, only decrease it.
|
||||
/// ---------------
|
||||
/// - Base Weight: 34.51 µs * .048 L µs
|
||||
/// - DB Weight:
|
||||
/// - Reads: EraElectionStatus, Ledger, Locks, [Origin Account]
|
||||
/// - Writes: [Origin Account], Locks, Ledger
|
||||
/// # </weight>
|
||||
#[weight = 500_000_000]
|
||||
fn rebond(origin, #[compact] value: BalanceOf<T>) {
|
||||
#[weight =
|
||||
35 * WEIGHT_PER_MICROS
|
||||
+ 50 * WEIGHT_PER_NANOS * (MAX_UNLOCKING_CHUNKS as Weight)
|
||||
+ T::DbWeight::get().reads_writes(3, 2)
|
||||
]
|
||||
fn rebond(origin, #[compact] value: BalanceOf<T>) -> DispatchResultWithPostInfo {
|
||||
ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed);
|
||||
let controller = ensure_signed(origin)?;
|
||||
let ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?;
|
||||
@@ -1767,13 +1935,44 @@ decl_module! {
|
||||
|
||||
let ledger = ledger.rebond(value);
|
||||
Self::update_ledger(&controller, &ledger);
|
||||
Ok(Some(
|
||||
35 * WEIGHT_PER_MICROS
|
||||
+ 50 * WEIGHT_PER_NANOS * (ledger.unlocking.len() as Weight)
|
||||
+ T::DbWeight::get().reads_writes(3, 2)
|
||||
).into())
|
||||
}
|
||||
|
||||
/// Set history_depth value.
|
||||
/// Set `HistoryDepth` value. This function will delete any history information
|
||||
/// when `HistoryDepth` is reduced.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - `new_history_depth`: The new history depth you would like to set.
|
||||
/// - `era_items_deleted`: The number of items that will be deleted by this dispatch.
|
||||
/// This should report all the storage items that will be deleted by clearing old
|
||||
/// era history. Needed to report an accurate weight for the dispatch. Trusted by
|
||||
/// `Root` to report an accurate number.
|
||||
///
|
||||
/// Origin must be root.
|
||||
#[weight = (500_000_000, DispatchClass::Operational)]
|
||||
fn set_history_depth(origin, #[compact] new_history_depth: EraIndex) {
|
||||
///
|
||||
/// # <weight>
|
||||
/// - E: Number of history depths removed, i.e. 10 -> 7 = 3
|
||||
/// - Base Weight: 29.13 * E µs
|
||||
/// - DB Weight:
|
||||
/// - Reads: Current Era, History Depth
|
||||
/// - Writes: History Depth
|
||||
/// - Clear Prefix Each: Era Stakers, EraStakersClipped, ErasValidatorPrefs
|
||||
/// - Writes Each: ErasValidatorReward, ErasRewardPoints, ErasTotalStake, ErasStartSessionIndex
|
||||
/// # </weight>
|
||||
#[weight = {
|
||||
let items = Weight::from(*_era_items_deleted);
|
||||
T::DbWeight::get().reads_writes(2, 1)
|
||||
.saturating_add(T::DbWeight::get().reads_writes(items, items))
|
||||
|
||||
}]
|
||||
fn set_history_depth(origin,
|
||||
#[compact] new_history_depth: EraIndex,
|
||||
#[compact] _era_items_deleted: u32,
|
||||
) {
|
||||
ensure_root(origin)?;
|
||||
if let Some(current_era) = Self::current_era() {
|
||||
HistoryDepth::mutate(|history_depth| {
|
||||
@@ -1794,10 +1993,27 @@ decl_module! {
|
||||
/// This can be called from any origin.
|
||||
///
|
||||
/// - `stash`: The stash account to reap. Its balance must be zero.
|
||||
#[weight = 0]
|
||||
fn reap_stash(_origin, stash: T::AccountId) {
|
||||
///
|
||||
/// # <weight>
|
||||
/// Complexity: O(S) where S is the number of slashing spans on the account.
|
||||
/// Base Weight: 75.94 + 2.396 * S µs
|
||||
/// DB Weight:
|
||||
/// - Reads: Stash Account, Bonded, Slashing Spans, Locks
|
||||
/// - Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators, Stash Account, Locks
|
||||
/// - Writes Each: SpanSlash * S
|
||||
/// # </weight>
|
||||
#[weight = T::DbWeight::get().reads_writes(4, 7)
|
||||
.saturating_add(76 * WEIGHT_PER_MICROS)
|
||||
.saturating_add(
|
||||
WEIGHT_PER_MICROS.saturating_mul(2).saturating_mul(Weight::from(*num_slashing_spans))
|
||||
)
|
||||
.saturating_add(T::DbWeight::get().writes(Weight::from(*num_slashing_spans)))
|
||||
// if slashing spans is non-zero, add 1 more write
|
||||
.saturating_add(T::DbWeight::get().writes(Weight::from(*num_slashing_spans).min(1)))
|
||||
]
|
||||
fn reap_stash(_origin, stash: T::AccountId, num_slashing_spans: u32) {
|
||||
ensure!(T::Currency::total_balance(&stash).is_zero(), Error::<T>::FundedTarget);
|
||||
Self::kill_stash(&stash)?;
|
||||
Self::kill_stash(&stash, num_slashing_spans)?;
|
||||
T::Currency::remove_lock(STAKING_ID, &stash);
|
||||
}
|
||||
|
||||
@@ -1939,12 +2155,18 @@ impl<T: Trait> Module<T> {
|
||||
///
|
||||
/// This data is used to efficiently evaluate election results. returns `true` if the operation
|
||||
/// is successful.
|
||||
fn create_stakers_snapshot() -> bool {
|
||||
fn create_stakers_snapshot() -> (bool, Weight) {
|
||||
let mut consumed_weight = 0;
|
||||
let mut add_db_reads_writes = |reads, writes| {
|
||||
consumed_weight += T::DbWeight::get().reads_writes(reads, writes);
|
||||
};
|
||||
let validators = <Validators<T>>::iter().map(|(v, _)| v).collect::<Vec<_>>();
|
||||
let mut nominators = <Nominators<T>>::iter().map(|(n, _)| n).collect::<Vec<_>>();
|
||||
|
||||
let num_validators = validators.len();
|
||||
let num_nominators = nominators.len();
|
||||
add_db_reads_writes((num_validators + num_nominators) as Weight, 0);
|
||||
|
||||
if
|
||||
num_validators > MAX_VALIDATORS ||
|
||||
num_nominators.saturating_add(num_validators) > MAX_NOMINATORS
|
||||
@@ -1957,14 +2179,15 @@ impl<T: Trait> Module<T> {
|
||||
num_nominators,
|
||||
MAX_NOMINATORS,
|
||||
);
|
||||
false
|
||||
(false, consumed_weight)
|
||||
} else {
|
||||
// all validators nominate themselves;
|
||||
nominators.extend(validators.clone());
|
||||
|
||||
<SnapshotValidators<T>>::put(validators);
|
||||
<SnapshotNominators<T>>::put(nominators);
|
||||
true
|
||||
add_db_reads_writes(0, 2);
|
||||
(true, consumed_weight)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2804,16 +3027,18 @@ impl<T: Trait> Module<T> {
|
||||
/// This is called:
|
||||
/// - after a `withdraw_unbond()` call that frees all of a stash's bonded balance.
|
||||
/// - through `reap_stash()` if the balance has fallen to zero (through slashing).
|
||||
fn kill_stash(stash: &T::AccountId) -> DispatchResult {
|
||||
let controller = Bonded::<T>::take(stash).ok_or(Error::<T>::NotStash)?;
|
||||
fn kill_stash(stash: &T::AccountId, num_slashing_spans: u32) -> DispatchResult {
|
||||
let controller = Bonded::<T>::get(stash).ok_or(Error::<T>::NotStash)?;
|
||||
|
||||
slashing::clear_stash_metadata::<T>(stash, num_slashing_spans)?;
|
||||
|
||||
Bonded::<T>::remove(stash);
|
||||
<Ledger<T>>::remove(&controller);
|
||||
|
||||
<Payee<T>>::remove(stash);
|
||||
<Validators<T>>::remove(stash);
|
||||
<Nominators<T>>::remove(stash);
|
||||
|
||||
slashing::clear_stash_metadata::<T>(stash);
|
||||
|
||||
system::Module::<T>::dec_ref(stash);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -27,7 +27,7 @@ use frame_support::{
|
||||
assert_ok, impl_outer_origin, parameter_types, impl_outer_dispatch, impl_outer_event,
|
||||
StorageValue, StorageMap, StorageDoubleMap, IterableStorageMap,
|
||||
traits::{Currency, Get, FindAuthor, OnFinalize, OnInitialize},
|
||||
weights::Weight,
|
||||
weights::{Weight, constants::RocksDbWeight},
|
||||
};
|
||||
use sp_io;
|
||||
use sp_phragmen::{
|
||||
@@ -36,7 +36,7 @@ use sp_phragmen::{
|
||||
};
|
||||
use crate::*;
|
||||
|
||||
const INIT_TIMESTAMP: u64 = 30_000;
|
||||
pub const INIT_TIMESTAMP: u64 = 30_000;
|
||||
|
||||
/// The AccountId alias in this test module.
|
||||
pub(crate) type AccountId = u64;
|
||||
@@ -211,7 +211,7 @@ impl frame_system::Trait for Test {
|
||||
type Event = MetaEvent;
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
type DbWeight = ();
|
||||
type DbWeight = RocksDbWeight;
|
||||
type BlockExecutionWeight = ();
|
||||
type ExtrinsicBaseWeight = ();
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
@@ -763,6 +763,18 @@ pub(crate) fn on_offence_now(
|
||||
on_offence_in_era(offenders, slash_fraction, now)
|
||||
}
|
||||
|
||||
pub(crate) fn add_slash(who: &AccountId) {
|
||||
on_offence_now(
|
||||
&[
|
||||
OffenceDetails {
|
||||
offender: (who.clone(), Staking::eras_stakers(Staking::active_era().unwrap().index, who.clone())),
|
||||
reporters: vec![],
|
||||
},
|
||||
],
|
||||
&[Perbill::from_percent(10)],
|
||||
);
|
||||
}
|
||||
|
||||
// winners will be chosen by simply their unweighted total backing stake. Nominator stake is
|
||||
// distributed evenly.
|
||||
pub(crate) fn horrible_phragmen_with_post_processing(
|
||||
|
||||
@@ -50,11 +50,11 @@
|
||||
|
||||
use super::{
|
||||
EraIndex, Trait, Module, Store, BalanceOf, Exposure, Perbill, SessionInterface,
|
||||
NegativeImbalanceOf, UnappliedSlash,
|
||||
NegativeImbalanceOf, UnappliedSlash, Error,
|
||||
};
|
||||
use sp_runtime::{traits::{Zero, Saturating}, RuntimeDebug};
|
||||
use sp_runtime::{traits::{Zero, Saturating}, RuntimeDebug, DispatchResult};
|
||||
use frame_support::{
|
||||
StorageMap, StorageDoubleMap,
|
||||
StorageMap, StorageDoubleMap, ensure,
|
||||
traits::{Currency, OnUnbalanced, Imbalance},
|
||||
};
|
||||
use sp_std::vec::Vec;
|
||||
@@ -100,7 +100,7 @@ pub struct SlashingSpans {
|
||||
impl SlashingSpans {
|
||||
// creates a new record of slashing spans for a stash, starting at the beginning
|
||||
// of the bonding period, relative to now.
|
||||
fn new(window_start: EraIndex) -> Self {
|
||||
pub(crate) fn new(window_start: EraIndex) -> Self {
|
||||
SlashingSpans {
|
||||
span_index: 0,
|
||||
last_start: window_start,
|
||||
@@ -115,7 +115,7 @@ impl SlashingSpans {
|
||||
// update the slashing spans to reflect the start of a new span at the era after `now`
|
||||
// returns `true` if a new span was started, `false` otherwise. `false` indicates
|
||||
// that internal state is unchanged.
|
||||
fn end_span(&mut self, now: EraIndex) -> bool {
|
||||
pub(crate) fn end_span(&mut self, now: EraIndex) -> bool {
|
||||
let next_start = now + 1;
|
||||
if next_start <= self.last_start { return false }
|
||||
|
||||
@@ -547,12 +547,19 @@ pub(crate) fn clear_era_metadata<T: Trait>(obsolete_era: EraIndex) {
|
||||
}
|
||||
|
||||
/// Clear slashing metadata for a dead account.
|
||||
pub(crate) fn clear_stash_metadata<T: Trait>(stash: &T::AccountId) {
|
||||
let spans = match <Module<T> as Store>::SlashingSpans::take(stash) {
|
||||
None => return,
|
||||
pub(crate) fn clear_stash_metadata<T: Trait>(
|
||||
stash: &T::AccountId,
|
||||
num_slashing_spans: u32,
|
||||
) -> DispatchResult {
|
||||
let spans = match <Module<T> as Store>::SlashingSpans::get(stash) {
|
||||
None => return Ok(()),
|
||||
Some(s) => s,
|
||||
};
|
||||
|
||||
ensure!(num_slashing_spans as usize >= spans.iter().count(), Error::<T>::IncorrectSlashingSpans);
|
||||
|
||||
<Module<T> as Store>::SlashingSpans::remove(stash);
|
||||
|
||||
// kill slashing-span metadata for account.
|
||||
//
|
||||
// this can only happen while the account is staked _if_ they are completely slashed.
|
||||
@@ -561,6 +568,8 @@ pub(crate) fn clear_stash_metadata<T: Trait>(stash: &T::AccountId) {
|
||||
for span in spans.iter() {
|
||||
<Module<T> as Store>::SpanSlash::remove(&(stash.clone(), span.index));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// apply the slash to a stash account, deducting any missing funds from the reward
|
||||
|
||||
@@ -24,7 +24,7 @@ use sp_runtime::{
|
||||
use sp_staking::offence::OffenceDetails;
|
||||
use frame_support::{
|
||||
assert_ok, assert_noop, StorageMap,
|
||||
traits::{Currency, ReservableCurrency, OnInitialize},
|
||||
traits::{Currency, ReservableCurrency, OnInitialize, OnFinalize},
|
||||
};
|
||||
use pallet_balances::Error as BalancesError;
|
||||
use substrate_test_utils::assert_eq_uvec;
|
||||
@@ -34,15 +34,19 @@ fn force_unstake_works() {
|
||||
ExtBuilder::default().build_and_execute(|| {
|
||||
// Account 11 is stashed and locked, and account 10 is the controller
|
||||
assert_eq!(Staking::bonded(&11), Some(10));
|
||||
// Adds 2 slashing spans
|
||||
add_slash(&11);
|
||||
// Cant transfer
|
||||
assert_noop!(
|
||||
Balances::transfer(Origin::signed(11), 1, 10),
|
||||
BalancesError::<Test, _>::LiquidityRestrictions
|
||||
);
|
||||
// Force unstake requires root.
|
||||
assert_noop!(Staking::force_unstake(Origin::signed(11), 11), BadOrigin);
|
||||
assert_noop!(Staking::force_unstake(Origin::signed(11), 11, 2), BadOrigin);
|
||||
// Force unstake needs correct number of slashing spans (for weight calculation)
|
||||
assert_noop!(Staking::force_unstake(Origin::signed(11), 11, 0), BadOrigin);
|
||||
// We now force them to unstake
|
||||
assert_ok!(Staking::force_unstake(Origin::ROOT, 11));
|
||||
assert_ok!(Staking::force_unstake(Origin::ROOT, 11, 2));
|
||||
// No longer bonded.
|
||||
assert_eq!(Staking::bonded(&11), None);
|
||||
// Transfer works.
|
||||
@@ -50,6 +54,24 @@ fn force_unstake_works() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kill_stash_works() {
|
||||
ExtBuilder::default().build_and_execute(|| {
|
||||
// Account 11 is stashed and locked, and account 10 is the controller
|
||||
assert_eq!(Staking::bonded(&11), Some(10));
|
||||
// Adds 2 slashing spans
|
||||
add_slash(&11);
|
||||
// Only can kill a stash account
|
||||
assert_noop!(Staking::kill_stash(&12, 0), Error::<Test>::NotStash);
|
||||
// Respects slashing span count
|
||||
assert_noop!(Staking::kill_stash(&11, 0), Error::<Test>::IncorrectSlashingSpans);
|
||||
// Correct inputs, everything works
|
||||
assert_ok!(Staking::kill_stash(&11, 2));
|
||||
// No longer bonded.
|
||||
assert_eq!(Staking::bonded(&11), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_setup_works() {
|
||||
// Verifies initial conditions of mock
|
||||
@@ -1023,7 +1045,10 @@ fn bond_extra_and_withdraw_unbonded_works() {
|
||||
unlocking: vec![],
|
||||
claimed_rewards: vec![],
|
||||
}));
|
||||
assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 11), Exposure { total: 1000, own: 1000, others: vec![] });
|
||||
assert_eq!(
|
||||
Staking::eras_stakers(Staking::active_era().unwrap().index, 11),
|
||||
Exposure { total: 1000, own: 1000, others: vec![] }
|
||||
);
|
||||
|
||||
// deposit the extra 100 units
|
||||
Staking::bond_extra(Origin::signed(11), 100).unwrap();
|
||||
@@ -1036,7 +1061,10 @@ fn bond_extra_and_withdraw_unbonded_works() {
|
||||
claimed_rewards: vec![],
|
||||
}));
|
||||
// Exposure is a snapshot! only updated after the next era update.
|
||||
assert_ne!(Staking::eras_stakers(Staking::active_era().unwrap().index, 11), Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] });
|
||||
assert_ne!(
|
||||
Staking::eras_stakers(Staking::active_era().unwrap().index, 11),
|
||||
Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] }
|
||||
);
|
||||
|
||||
// trigger next era.
|
||||
mock::start_era(2);
|
||||
@@ -1051,34 +1079,68 @@ fn bond_extra_and_withdraw_unbonded_works() {
|
||||
claimed_rewards: vec![],
|
||||
}));
|
||||
// Exposure is now updated.
|
||||
assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 11), Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] });
|
||||
assert_eq!(
|
||||
Staking::eras_stakers(Staking::active_era().unwrap().index, 11),
|
||||
Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] }
|
||||
);
|
||||
|
||||
// Unbond almost all of the funds in stash.
|
||||
Staking::unbond(Origin::signed(10), 1000).unwrap();
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11, total: 1000 + 100, active: 100, unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 3}], claimed_rewards: vec![] })
|
||||
assert_eq!(
|
||||
Staking::ledger(&10),
|
||||
Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000 + 100,
|
||||
active: 100,
|
||||
unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 3}],
|
||||
claimed_rewards: vec![]
|
||||
}),
|
||||
);
|
||||
|
||||
// Attempting to free the balances now will fail. 2 eras need to pass.
|
||||
Staking::withdraw_unbonded(Origin::signed(10)).unwrap();
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11, total: 1000 + 100, active: 100, unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 3}], claimed_rewards: vec![] }));
|
||||
assert_ok!(Staking::withdraw_unbonded(Origin::signed(10), 0));
|
||||
assert_eq!(
|
||||
Staking::ledger(&10),
|
||||
Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000 + 100,
|
||||
active: 100,
|
||||
unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 3}],
|
||||
claimed_rewards: vec![]
|
||||
}),
|
||||
);
|
||||
|
||||
// trigger next era.
|
||||
mock::start_era(3);
|
||||
|
||||
// nothing yet
|
||||
Staking::withdraw_unbonded(Origin::signed(10)).unwrap();
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11, total: 1000 + 100, active: 100, unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 3}], claimed_rewards: vec![] }));
|
||||
assert_ok!(Staking::withdraw_unbonded(Origin::signed(10), 0));
|
||||
assert_eq!(
|
||||
Staking::ledger(&10),
|
||||
Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000 + 100,
|
||||
active: 100,
|
||||
unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 3}],
|
||||
claimed_rewards: vec![]
|
||||
}),
|
||||
);
|
||||
|
||||
// trigger next era.
|
||||
mock::start_era(5);
|
||||
|
||||
Staking::withdraw_unbonded(Origin::signed(10)).unwrap();
|
||||
assert_ok!(Staking::withdraw_unbonded(Origin::signed(10), 0));
|
||||
// Now the value is free and the staking ledger is updated.
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11, total: 100, active: 100, unlocking: vec![], claimed_rewards: vec![] }));
|
||||
assert_eq!(
|
||||
Staking::ledger(&10),
|
||||
Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 100,
|
||||
active: 100,
|
||||
unlocking: vec![],
|
||||
claimed_rewards: vec![]
|
||||
}),
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1101,7 +1163,7 @@ fn too_many_unbond_calls_should_not_work() {
|
||||
|
||||
assert_noop!(Staking::unbond(Origin::signed(10), 1), Error::<Test>::NoMoreChunks);
|
||||
// free up.
|
||||
assert_ok!(Staking::withdraw_unbonded(Origin::signed(10)));
|
||||
assert_ok!(Staking::withdraw_unbonded(Origin::signed(10), 0));
|
||||
|
||||
// Can add again.
|
||||
assert_ok!(Staking::unbond(Origin::signed(10), 1));
|
||||
@@ -1449,7 +1511,7 @@ fn on_free_balance_zero_stash_removes_validator() {
|
||||
assert_eq!(Balances::total_balance(&11), 0);
|
||||
|
||||
// Reap the stash
|
||||
assert_ok!(Staking::reap_stash(Origin::NONE, 11));
|
||||
assert_ok!(Staking::reap_stash(Origin::NONE, 11, 0));
|
||||
|
||||
// Check storage items do not exist
|
||||
assert!(!<Ledger<Test>>::contains_key(&10));
|
||||
@@ -1505,7 +1567,7 @@ fn on_free_balance_zero_stash_removes_nominator() {
|
||||
assert_eq!(Balances::total_balance(&11), 0);
|
||||
|
||||
// Reap the stash
|
||||
assert_ok!(Staking::reap_stash(Origin::NONE, 11));
|
||||
assert_ok!(Staking::reap_stash(Origin::NONE, 11, 0));
|
||||
|
||||
// Check storage items do not exist
|
||||
assert!(!<Ledger<Test>>::contains_key(&10));
|
||||
@@ -1619,14 +1681,14 @@ fn bond_with_no_staked_value() {
|
||||
mock::start_era(2);
|
||||
|
||||
// not yet removed.
|
||||
assert_ok!(Staking::withdraw_unbonded(Origin::signed(2)));
|
||||
assert_ok!(Staking::withdraw_unbonded(Origin::signed(2), 0));
|
||||
assert!(Staking::ledger(2).is_some());
|
||||
assert_eq!(Balances::locks(&1)[0].amount, 5);
|
||||
|
||||
mock::start_era(3);
|
||||
|
||||
// poof. Account 1 is removed from the staking system.
|
||||
assert_ok!(Staking::withdraw_unbonded(Origin::signed(2)));
|
||||
assert_ok!(Staking::withdraw_unbonded(Origin::signed(2), 0));
|
||||
assert!(Staking::ledger(2).is_none());
|
||||
assert_eq!(Balances::locks(&1).len(), 0);
|
||||
});
|
||||
@@ -2270,7 +2332,12 @@ fn garbage_collection_after_slashing() {
|
||||
assert_eq!(Balances::free_balance(11), 0);
|
||||
assert_eq!(Balances::total_balance(&11), 0);
|
||||
|
||||
assert_ok!(Staking::reap_stash(Origin::NONE, 11));
|
||||
let slashing_spans = <Staking as crate::Store>::SlashingSpans::get(&11).unwrap();
|
||||
assert_eq!(slashing_spans.iter().count(), 2);
|
||||
|
||||
// reap_stash respects num_slashing_spans so that weight is accurate
|
||||
assert_noop!(Staking::reap_stash(Origin::NONE, 11, 0), Error::<Test>::IncorrectSlashingSpans);
|
||||
assert_ok!(Staking::reap_stash(Origin::NONE, 11, 2));
|
||||
|
||||
assert!(<Staking as crate::Store>::SlashingSpans::get(&11).is_none());
|
||||
assert_eq!(<Staking as crate::Store>::SpanSlash::get(&(11, 0)).amount_slashed(), &0);
|
||||
@@ -2705,7 +2772,7 @@ mod offchain_phragmen {
|
||||
|
||||
/// setup a new set of validators and nominator storage items independent of the parent mock
|
||||
/// file. This produces a edge graph that can be reduced.
|
||||
fn build_offchain_phragmen_test_ext() {
|
||||
pub fn build_offchain_phragmen_test_ext() {
|
||||
for i in (10..=40).step_by(10) {
|
||||
// Note: we respect the convention of the mock (10, 11 pairs etc.) since these accounts
|
||||
// have corresponding keys in session which makes everything more ergonomic and
|
||||
@@ -4092,16 +4159,16 @@ fn test_max_nominator_rewarded_per_validator_and_cant_steal_someone_else_reward(
|
||||
fn set_history_depth_works() {
|
||||
ExtBuilder::default().build_and_execute(|| {
|
||||
mock::start_era(10);
|
||||
Staking::set_history_depth(Origin::ROOT, 20).unwrap();
|
||||
Staking::set_history_depth(Origin::ROOT, 20, 0).unwrap();
|
||||
assert!(<Staking as Store>::ErasTotalStake::contains_key(10 - 4));
|
||||
assert!(<Staking as Store>::ErasTotalStake::contains_key(10 - 5));
|
||||
Staking::set_history_depth(Origin::ROOT, 4).unwrap();
|
||||
Staking::set_history_depth(Origin::ROOT, 4, 0).unwrap();
|
||||
assert!(<Staking as Store>::ErasTotalStake::contains_key(10 - 4));
|
||||
assert!(!<Staking as Store>::ErasTotalStake::contains_key(10 - 5));
|
||||
Staking::set_history_depth(Origin::ROOT, 3).unwrap();
|
||||
Staking::set_history_depth(Origin::ROOT, 3, 0).unwrap();
|
||||
assert!(!<Staking as Store>::ErasTotalStake::contains_key(10 - 4));
|
||||
assert!(!<Staking as Store>::ErasTotalStake::contains_key(10 - 5));
|
||||
Staking::set_history_depth(Origin::ROOT, 8).unwrap();
|
||||
Staking::set_history_depth(Origin::ROOT, 8, 0).unwrap();
|
||||
assert!(!<Staking as Store>::ErasTotalStake::contains_key(10 - 4));
|
||||
assert!(!<Staking as Store>::ErasTotalStake::contains_key(10 - 5));
|
||||
});
|
||||
@@ -4598,3 +4665,36 @@ fn migrate_era_should_handle_errors_2() {
|
||||
assert_eq_error_rate!(Balances::total_balance(&101), init_balance_101, 2);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn on_initialize_weight_is_correct() {
|
||||
ExtBuilder::default().has_stakers(false).build_and_execute(|| {
|
||||
assert_eq!(Validators::<Test>::iter().count(), 0);
|
||||
assert_eq!(Nominators::<Test>::iter().count(), 0);
|
||||
// When this pallet has nothing, we do 4 reads each block
|
||||
let base_weight = <Test as frame_system::Trait>::DbWeight::get().reads(4);
|
||||
assert_eq!(base_weight, Staking::on_initialize(0));
|
||||
});
|
||||
|
||||
ExtBuilder::default()
|
||||
.offchain_phragmen_ext()
|
||||
.validator_count(4)
|
||||
.has_stakers(false)
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
crate::tests::offchain_phragmen::build_offchain_phragmen_test_ext();
|
||||
run_to_block(11);
|
||||
Staking::on_finalize(System::block_number());
|
||||
System::set_block_number((System::block_number() + 1).into());
|
||||
Timestamp::set_timestamp(System::block_number() * 1000 + INIT_TIMESTAMP);
|
||||
Session::on_initialize(System::block_number());
|
||||
|
||||
assert_eq!(Validators::<Test>::iter().count(), 4);
|
||||
assert_eq!(Nominators::<Test>::iter().count(), 5);
|
||||
// With 4 validators and 5 nominator, we should increase weight by:
|
||||
// - (4 + 5) reads
|
||||
// - 3 Writes
|
||||
let final_weight = <Test as frame_system::Trait>::DbWeight::get().reads_writes(4 + 9, 3);
|
||||
assert_eq!(final_weight, Staking::on_initialize(System::block_number()));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ use sp_runtime::{
|
||||
};
|
||||
use crate::dispatch::Parameter;
|
||||
use crate::storage::StorageMap;
|
||||
use crate::weights::Weight;
|
||||
use impl_trait_for_tuples::impl_for_tuples;
|
||||
|
||||
/// An abstraction of a value stored within storage, but possibly as part of a larger composite
|
||||
@@ -147,12 +148,19 @@ pub trait EstimateNextSessionRotation<BlockNumber> {
|
||||
///
|
||||
/// None should be returned if the estimation fails to come to an answer
|
||||
fn estimate_next_session_rotation(now: BlockNumber) -> Option<BlockNumber>;
|
||||
|
||||
/// Return the weight of calling `estimate_next_session_rotation`
|
||||
fn weight(now: BlockNumber) -> Weight;
|
||||
}
|
||||
|
||||
impl<BlockNumber: Bounded> EstimateNextSessionRotation<BlockNumber> for () {
|
||||
fn estimate_next_session_rotation(_: BlockNumber) -> Option<BlockNumber> {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn weight(_: BlockNumber) -> Weight {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that can estimate at which block the next `new_session` will be triggered. This must
|
||||
@@ -160,12 +168,19 @@ impl<BlockNumber: Bounded> EstimateNextSessionRotation<BlockNumber> for () {
|
||||
pub trait EstimateNextNewSession<BlockNumber> {
|
||||
/// Return the block number at which the next new session is estimated to happen.
|
||||
fn estimate_next_new_session(now: BlockNumber) -> Option<BlockNumber>;
|
||||
|
||||
/// Return the weight of calling `estimate_next_new_session`
|
||||
fn weight(now: BlockNumber) -> Weight;
|
||||
}
|
||||
|
||||
impl<BlockNumber: Bounded> EstimateNextNewSession<BlockNumber> for () {
|
||||
fn estimate_next_new_session(_: BlockNumber) -> Option<BlockNumber> {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn weight(_: BlockNumber) -> Weight {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Anything that can have a `::len()` method.
|
||||
|
||||
Reference in New Issue
Block a user