Benchmarks for offences pallet. (#5851)

* really rough mock runtime

* start to work on offences

* Make sure to start the session.

* Update to latest master.

* Add verify.

* Fix on_initialize benchmark.

* Add grandpa offence.

* Add Babe offence benchmarking.

* Enable babe test.

* Address review grumbles.

* Address review grumbles.

* Address review grumbles part 1/2

* use currency trait

* features

* Check events explicitly.

* Auto-impl tuple converter.

* Removed dead code.

* add test feature flag

* dont use std

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Tomasz Drwięga
2020-05-06 14:27:47 +02:00
committed by GitHub
parent 11f144ee65
commit 66c02fe651
8 changed files with 558 additions and 76 deletions
+7
View File
@@ -4393,11 +4393,18 @@ dependencies = [
"frame-benchmarking",
"frame-support",
"frame-system",
"pallet-babe",
"pallet-balances",
"pallet-grandpa",
"pallet-im-online",
"pallet-offences",
"pallet-session",
"pallet-staking",
"pallet-staking-reward-curve",
"pallet-timestamp",
"parity-scale-codec",
"serde",
"sp-core",
"sp-io",
"sp-runtime",
"sp-staking",
+6 -7
View File
@@ -18,7 +18,7 @@
//! from VRF outputs and manages epoch transitions.
#![cfg_attr(not(feature = "std"), no_std)]
#![forbid(unused_must_use, unsafe_code, unused_variables, unused_must_use)]
#![warn(unused_must_use, unsafe_code, unused_variables, unused_must_use)]
use pallet_timestamp;
@@ -268,19 +268,18 @@ impl<T: Trait> pallet_session::ShouldEndSession<T::BlockNumber> for Module<T> {
}
}
// TODO [slashing]: @marcio use this, remove the dead_code annotation.
/// A BABE equivocation offence report.
///
/// When a validator released two or more blocks at the same slot.
struct BabeEquivocationOffence<FullIdentification> {
pub struct BabeEquivocationOffence<FullIdentification> {
/// A babe slot number in which this incident happened.
slot: u64,
pub slot: u64,
/// The session index in which the incident happened.
session_index: SessionIndex,
pub session_index: SessionIndex,
/// The size of the validator set at the time of the offence.
validator_set_count: u32,
pub validator_set_count: u32,
/// The authority that produced the equivocation.
offender: FullIdentification,
pub offender: FullIdentification,
}
impl<FullIdentification: Clone> Offence<FullIdentification> for BabeEquivocationOffence<FullIdentification> {
+10 -9
View File
@@ -456,23 +456,24 @@ impl<T: Trait> pallet_finality_tracker::OnFinalizationStalled<T::BlockNumber> fo
/// A round number and set id which point on the time of an offence.
#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Encode, Decode)]
struct GrandpaTimeSlot {
pub struct GrandpaTimeSlot {
// The order of these matters for `derive(Ord)`.
set_id: SetId,
round: RoundNumber,
/// Grandpa Set ID.
pub set_id: SetId,
/// Round number.
pub round: RoundNumber,
}
// TODO [slashing]: Integrate this.
/// A grandpa equivocation offence report.
struct GrandpaEquivocationOffence<FullIdentification> {
pub struct GrandpaEquivocationOffence<FullIdentification> {
/// Time slot at which this incident happened.
time_slot: GrandpaTimeSlot,
pub time_slot: GrandpaTimeSlot,
/// The session index in which the incident happened.
session_index: SessionIndex,
pub session_index: SessionIndex,
/// The size of the validator set at the time of the offence.
validator_set_count: u32,
pub validator_set_count: u32,
/// The authority which produced this equivocation.
offender: FullIdentification,
pub offender: FullIdentification,
}
impl<FullIdentification: Clone> Offence<FullIdentification> for GrandpaEquivocationOffence<FullIdentification> {
@@ -13,31 +13,44 @@ targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false }
sp-std = { version = "2.0.0-dev", default-features = false, path = "../../../primitives/std" }
sp-staking = { version = "2.0.0-dev", default-features = false, path = "../../../primitives/staking" }
sp-runtime = { version = "2.0.0-dev", default-features = false, path = "../../../primitives/runtime" }
frame-benchmarking = { version = "2.0.0-dev", default-features = false, path = "../../benchmarking" }
frame-system = { version = "2.0.0-dev", default-features = false, path = "../../system" }
frame-support = { version = "2.0.0-dev", default-features = false, path = "../../support" }
frame-system = { version = "2.0.0-dev", default-features = false, path = "../../system" }
pallet-babe = { version = "2.0.0-dev", default-features = false, path = "../../babe" }
pallet-balances = { version = "2.0.0-dev", default-features = false, path = "../../balances" }
pallet-grandpa = { version = "2.0.0-dev", default-features = false, path = "../../grandpa" }
pallet-im-online = { version = "2.0.0-dev", default-features = false, path = "../../im-online" }
pallet-offences = { version = "2.0.0-dev", default-features = false, features = ["runtime-benchmarks"], path = "../../offences" }
pallet-staking = { version = "2.0.0-dev", default-features = false, features = ["runtime-benchmarks"], path = "../../staking" }
pallet-session = { version = "2.0.0-dev", default-features = false, path = "../../session" }
pallet-staking = { version = "2.0.0-dev", default-features = false, features = ["runtime-benchmarks"], path = "../../staking" }
sp-io = { path = "../../../primitives/io", default-features = false, version = "2.0.0-dev"}
sp-runtime = { version = "2.0.0-dev", default-features = false, path = "../../../primitives/runtime" }
sp-staking = { version = "2.0.0-dev", default-features = false, path = "../../../primitives/staking" }
sp-std = { version = "2.0.0-dev", default-features = false, path = "../../../primitives/std" }
[dev-dependencies]
codec = { package = "parity-scale-codec", version = "1.3.0", features = ["derive"] }
pallet-staking-reward-curve = { version = "2.0.0-dev", path = "../../staking/reward-curve" }
pallet-timestamp = { version = "2.0.0-dev", path = "../../timestamp" }
serde = { version = "1.0.101" }
sp-core = { version = "2.0.0-dev", path = "../../../primitives/core" }
sp-io ={ path = "../../../primitives/io", version = "2.0.0-dev"}
[features]
default = ["std"]
std = [
"sp-runtime/std",
"sp-std/std",
"sp-staking/std",
"frame-benchmarking/std",
"frame-support/std",
"frame-system/std",
"pallet-offences/std",
"pallet-babe/std",
"pallet-balances/std",
"pallet-grandpa/std",
"pallet-im-online/std",
"pallet-staking/std",
"pallet-offences/std",
"pallet-session/std",
"pallet-staking/std",
"sp-runtime/std",
"sp-staking/std",
"sp-std/std",
"sp-io/std",
]
+291 -47
View File
@@ -18,28 +18,32 @@
#![cfg_attr(not(feature = "std"), no_std)]
mod mock;
use sp_std::prelude::*;
use sp_std::vec;
use frame_system::RawOrigin;
use frame_system::{RawOrigin, Module as System, Trait as SystemTrait};
use frame_benchmarking::{benchmarks, account};
use frame_support::traits::{Currency, OnInitialize};
use sp_runtime::{Perbill, traits::{Convert, StaticLookup}};
use sp_staking::offence::ReportOffence;
use sp_runtime::{Perbill, traits::{Convert, StaticLookup, Saturating, UniqueSaturatedInto}};
use sp_staking::offence::{ReportOffence, Offence, OffenceDetails};
use pallet_balances::{Trait as BalancesTrait};
use pallet_babe::BabeEquivocationOffence;
use pallet_grandpa::{GrandpaEquivocationOffence, GrandpaTimeSlot};
use pallet_im_online::{Trait as ImOnlineTrait, Module as ImOnline, UnresponsivenessOffence};
use pallet_offences::{Trait as OffencesTrait, Module as Offences};
use pallet_session::historical::{Trait as HistoricalTrait, IdentificationTuple};
use pallet_session::{Trait as SessionTrait, SessionManager};
use pallet_staking::{
Module as Staking, Trait as StakingTrait, RewardDestination, ValidatorPrefs,
Exposure, IndividualExposure, ElectionStatus
Exposure, IndividualExposure, ElectionStatus, MAX_NOMINATIONS, Event as StakingEvent
};
use pallet_session::Trait as SessionTrait;
use pallet_session::historical::{Trait as HistoricalTrait, IdentificationTuple};
const SEED: u32 = 0;
const MAX_USERS: u32 = 1000;
const MAX_REPORTERS: u32 = 100;
const MAX_OFFENDERS: u32 = 100;
const MAX_NOMINATORS: u32 = 100;
@@ -47,15 +51,54 @@ const MAX_DEFERRED_OFFENCES: u32 = 100;
pub struct Module<T: Trait>(Offences<T>);
pub trait Trait: SessionTrait + StakingTrait + OffencesTrait + ImOnlineTrait + HistoricalTrait {}
pub trait Trait:
SessionTrait
+ StakingTrait
+ OffencesTrait
+ ImOnlineTrait
+ HistoricalTrait
+ BalancesTrait
+ IdTupleConvert<Self>
{}
fn create_offender<T: Trait>(n: u32, nominators: u32) -> Result<T::AccountId, &'static str> {
/// A helper trait to make sure we can convert `IdentificationTuple` coming from historical
/// and the one required by offences.
pub trait IdTupleConvert<T: HistoricalTrait + OffencesTrait> {
/// Convert identification tuple from `historical` trait to the one expected by `offences`.
fn convert(id: IdentificationTuple<T>) -> <T as OffencesTrait>::IdentificationTuple;
}
impl<T: HistoricalTrait + OffencesTrait> IdTupleConvert<T> for T where
<T as OffencesTrait>::IdentificationTuple: From<IdentificationTuple<T>>
{
fn convert(id: IdentificationTuple<T>) -> <T as OffencesTrait>::IdentificationTuple {
id.into()
}
}
type LookupSourceOf<T> = <<T as SystemTrait>::Lookup as StaticLookup>::Source;
type BalanceOf<T> = <<T as StakingTrait>::Currency as Currency<<T as SystemTrait>::AccountId>>::Balance;
struct Offender<T: Trait> {
pub controller: T::AccountId,
pub stash: T::AccountId,
pub nominator_stashes: Vec<T::AccountId>,
}
fn bond_amount<T: Trait>() -> BalanceOf<T> {
T::Currency::minimum_balance().saturating_mul(10_000.into())
}
fn create_offender<T: Trait>(n: u32, nominators: u32) -> Result<Offender<T>, &'static str> {
let stash: T::AccountId = account("stash", n, SEED);
let controller: T::AccountId = account("controller", n, SEED);
let controller_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(controller.clone());
let controller_lookup: LookupSourceOf<T> = T::Lookup::unlookup(controller.clone());
let reward_destination = RewardDestination::Staked;
let amount = T::Currency::minimum_balance();
let raw_amount = bond_amount::<T>();
// add twice as much balance to prevent the account from being killed.
let free_amount = raw_amount.saturating_mul(2.into());
T::Currency::make_free_balance_be(&stash, free_amount);
let amount: BalanceOf<T> = raw_amount.into();
Staking::<T>::bond(
RawOrigin::Signed(stash.clone()).into(),
controller_lookup.clone(),
@@ -69,27 +112,29 @@ fn create_offender<T: Trait>(n: u32, nominators: u32) -> Result<T::AccountId, &'
Staking::<T>::validate(RawOrigin::Signed(controller.clone()).into(), validator_prefs)?;
let mut individual_exposures = vec![];
let mut nominator_stashes = vec![];
// Create n nominators
for i in 0 .. nominators {
let nominator_stash: T::AccountId = account("nominator stash", n * MAX_NOMINATORS + i, SEED);
let nominator_controller: T::AccountId = account("nominator controller", n * MAX_NOMINATORS + i, SEED);
let nominator_controller_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(nominator_controller.clone());
let nominator_controller_lookup: LookupSourceOf<T> = T::Lookup::unlookup(nominator_controller.clone());
T::Currency::make_free_balance_be(&nominator_stash, free_amount.into());
Staking::<T>::bond(
RawOrigin::Signed(nominator_stash.clone()).into(),
nominator_controller_lookup.clone(),
amount,
amount.clone(),
reward_destination,
)?;
let selected_validators: Vec<<T::Lookup as StaticLookup>::Source> = vec![controller_lookup.clone()];
let selected_validators: Vec<LookupSourceOf<T>> = vec![controller_lookup.clone()];
Staking::<T>::nominate(RawOrigin::Signed(nominator_controller.clone()).into(), selected_validators)?;
individual_exposures.push(IndividualExposure {
who: nominator_controller.clone(),
who: nominator_stash.clone(),
value: amount.clone(),
});
nominator_stashes.push(nominator_stash.clone());
}
let exposure = Exposure {
@@ -100,76 +145,275 @@ fn create_offender<T: Trait>(n: u32, nominators: u32) -> Result<T::AccountId, &'
let current_era = 0u32;
Staking::<T>::add_era_stakers(current_era.into(), stash.clone().into(), exposure);
Ok(controller)
Ok(Offender { controller, stash, nominator_stashes })
}
fn make_offenders<T: Trait>(num_offenders: u32, num_nominators: u32) -> Result<Vec<IdentificationTuple<T>>, &'static str> {
let mut offenders: Vec<T::AccountId> = vec![];
fn make_offenders<T: Trait>(num_offenders: u32, num_nominators: u32) -> Result<
(Vec<IdentificationTuple<T>>, Vec<Offender<T>>),
&'static str
> {
Staking::<T>::new_session(0);
let mut offenders = vec![];
for i in 0 .. num_offenders {
let offender = create_offender::<T>(i, num_nominators)?;
let offender = create_offender::<T>(i + 1, num_nominators)?;
offenders.push(offender);
}
Ok(offenders.iter()
.map(|id|
<T as SessionTrait>::ValidatorIdOf::convert(id.clone())
Staking::<T>::start_session(0);
let id_tuples = offenders.iter()
.map(|offender|
<T as SessionTrait>::ValidatorIdOf::convert(offender.controller.clone())
.expect("failed to get validator id from account id"))
.map(|validator_id|
<T as HistoricalTrait>::FullIdentificationOf::convert(validator_id.clone())
.map(|full_id| (validator_id, full_id))
.expect("failed to convert validator id to full identification"))
.collect::<Vec<IdentificationTuple<T>>>())
.collect::<Vec<IdentificationTuple<T>>>();
Ok((id_tuples, offenders))
}
#[cfg(test)]
fn check_events<T: Trait, I: Iterator<Item = <T as SystemTrait>::Event>>(expected: I) {
let events = System::<T>::events() .into_iter()
.map(|frame_system::EventRecord { event, .. }| event).collect::<Vec<_>>();
let expected = expected.collect::<Vec<_>>();
let lengths = (events.len(), expected.len());
let length_mismatch = if lengths.0 != lengths.1 {
fn pretty<D: std::fmt::Debug>(header: &str, ev: &[D]) {
println!("{}", header);
for (idx, ev) in ev.iter().enumerate() {
println!("\t[{:04}] {:?}", idx, ev);
}
}
pretty("--Got:", &events);
pretty("--Expected:", &expected);
format!("Mismatching length. Got: {}, expected: {}", lengths.0, lengths.1)
} else { Default::default() };
for (idx, (a, b)) in events.into_iter().zip(expected).enumerate() {
assert_eq!(a, b, "Mismatch at: {}. {}", idx, length_mismatch);
}
if !length_mismatch.is_empty() {
panic!(length_mismatch);
}
}
benchmarks! {
_ {
let u in 1 .. MAX_USERS => ();
let r in 1 .. MAX_REPORTERS => ();
let o in 1 .. MAX_OFFENDERS => ();
let n in 1 .. MAX_NOMINATORS => ();
let d in 1 .. MAX_DEFERRED_OFFENCES => ();
}
_ { }
report_offence {
let r in ...;
let o in ...;
let n in ...;
report_offence_im_online {
let r in 1 .. MAX_REPORTERS;
// we skip 1 offender, because in such case there is no slashing
let o in 2 .. MAX_OFFENDERS;
let n in 0 .. MAX_NOMINATORS.min(MAX_NOMINATIONS as u32);
// Make r reporters
let mut reporters = vec![];
for i in 0 .. r {
let reporter = account("reporter", i, SEED);
reporters.push(reporter);
}
let offenders = make_offenders::<T>(o, n).expect("failed to create offenders");
let keys = ImOnline::<T>::keys();
// make sure reporters actually get rewarded
Staking::<T>::set_slash_reward_fraction(Perbill::one());
let (offenders, raw_offenders) = make_offenders::<T>(o, n)?;
let keys = ImOnline::<T>::keys();
let validator_set_count = keys.len() as u32;
let slash_fraction = UnresponsivenessOffence::<T::AccountId>::slash_fraction(
offenders.len() as u32, validator_set_count,
);
let offence = UnresponsivenessOffence {
session_index: 0,
validator_set_count: keys.len() as u32,
validator_set_count,
offenders,
};
assert_eq!(System::<T>::event_count(), 0);
}: {
let _ = <T as ImOnlineTrait>::ReportUnresponsiveness::report_offence(reporters, offence);
let _ = <T as ImOnlineTrait>::ReportUnresponsiveness::report_offence(
reporters.clone(),
offence
);
}
verify {
// make sure the report was not deferred
assert!(Offences::<T>::deferred_offences().is_empty());
let slash_amount = slash_fraction * bond_amount::<T>().unique_saturated_into() as u32;
let reward_amount = slash_amount * (1 + n) / 2;
let mut slash_events = raw_offenders.into_iter()
.flat_map(|offender| {
core::iter::once(offender.stash).chain(offender.nominator_stashes.into_iter())
})
.map(|stash| <T as StakingTrait>::Event::from(
StakingEvent::<T>::Slash(stash, BalanceOf::<T>::from(slash_amount))
))
.collect::<Vec<_>>();
let reward_events = reporters.into_iter()
.flat_map(|reporter| vec![
frame_system::Event::<T>::NewAccount(reporter.clone()).into(),
<T as BalancesTrait>::Event::from(
pallet_balances::Event::<T>::Endowed(reporter.clone(), (reward_amount / r).into())
).into()
]);
// rewards are applied after first offender and it's nominators
let slash_rest = slash_events.split_off(1 + n as usize);
// make sure that all slashes have been applied
#[cfg(test)]
check_events::<T, _>(
std::iter::empty()
.chain(slash_events.into_iter().map(Into::into))
.chain(reward_events)
.chain(slash_rest.into_iter().map(Into::into))
.chain(std::iter::once(<T as OffencesTrait>::Event::from(
pallet_offences::Event::Offence(
UnresponsivenessOffence::<T>::ID,
0_u32.to_le_bytes().to_vec(),
true
)
).into()))
);
}
report_offence_grandpa {
let r in 1 .. MAX_REPORTERS;
let n in 0 .. MAX_NOMINATORS.min(MAX_NOMINATIONS as u32);
let o = 1;
// Make r reporters
let mut reporters = vec![];
for i in 0 .. r {
let reporter = account("reporter", i, SEED);
reporters.push(reporter);
}
// make sure reporters actually get rewarded
Staking::<T>::set_slash_reward_fraction(Perbill::one());
let (mut offenders, raw_offenders) = make_offenders::<T>(o, n)?;
let keys = ImOnline::<T>::keys();
let offence = GrandpaEquivocationOffence {
time_slot: GrandpaTimeSlot { set_id: 0, round: 0 },
session_index: 0,
validator_set_count: keys.len() as u32,
offender: T::convert(offenders.pop().unwrap()),
};
assert_eq!(System::<T>::event_count(), 0);
}: {
let _ = Offences::<T>::report_offence(reporters, offence);
}
verify {
// make sure the report was not deferred
assert!(Offences::<T>::deferred_offences().is_empty());
// make sure that all slashes have been applied
assert_eq!(
System::<T>::event_count(), 0
+ 1 // offence
+ 2 * r // reporter (reward + endowment)
+ o // offenders slashed
+ o * n // nominators slashed
);
}
report_offence_babe {
let r in 1 .. MAX_REPORTERS;
let n in 0 .. MAX_NOMINATORS.min(MAX_NOMINATIONS as u32);
let o = 1;
// Make r reporters
let mut reporters = vec![];
for i in 0 .. r {
let reporter = account("reporter", i, SEED);
reporters.push(reporter);
}
// make sure reporters actually get rewarded
Staking::<T>::set_slash_reward_fraction(Perbill::one());
let (mut offenders, raw_offenders) = make_offenders::<T>(o, n)?;
let keys = ImOnline::<T>::keys();
let offence = BabeEquivocationOffence {
slot: 0,
session_index: 0,
validator_set_count: keys.len() as u32,
offender: T::convert(offenders.pop().unwrap()),
};
assert_eq!(System::<T>::event_count(), 0);
}: {
let _ = Offences::<T>::report_offence(reporters, offence);
}
verify {
// make sure the report was not deferred
assert!(Offences::<T>::deferred_offences().is_empty());
// make sure that all slashes have been applied
assert_eq!(
System::<T>::event_count(), 0
+ 1 // offence
+ 2 * r // reporter (reward + endowment)
+ o // offenders slashed
+ o * n // nominators slashed
);
}
on_initialize {
let d in ...;
let d in 1 .. MAX_DEFERRED_OFFENCES;
let o = 10;
let n = 100;
Staking::<T>::put_election_status(ElectionStatus::Closed);
let mut deferred_offences = vec![];
let offenders = make_offenders::<T>(o, n)?.0;
let offence_details = offenders.into_iter()
.map(|offender| OffenceDetails {
offender: T::convert(offender),
reporters: vec![],
})
.collect::<Vec<_>>();
for i in 0 .. d {
deferred_offences.push((vec![], vec![], 0u32));
let fractions = offence_details.iter()
.map(|_| Perbill::from_percent(100 * (i + 1) / MAX_DEFERRED_OFFENCES))
.collect::<Vec<_>>();
deferred_offences.push((offence_details.clone(), fractions.clone(), 0u32));
}
Offences::<T>::set_deferred_offences(deferred_offences);
assert!(!Offences::<T>::deferred_offences().is_empty());
}: {
Offences::<T>::on_initialize(u.into());
Offences::<T>::on_initialize(0.into());
}
verify {
// make sure that all deferred offences were reported with Ok status.
assert!(Offences::<T>::deferred_offences().is_empty());
assert_eq!(
System::<T>::event_count(), d * (0
+ o // offenders slashed
+ o * n // nominators slashed
));
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::{new_test_ext, Test};
use frame_support::assert_ok;
#[test]
fn test_benchmarks() {
new_test_ext().execute_with(|| {
assert_ok!(test_benchmark_report_offence_im_online::<Test>());
assert_ok!(test_benchmark_report_offence_grandpa::<Test>());
assert_ok!(test_benchmark_report_offence_babe::<Test>());
assert_ok!(test_benchmark_on_initialize::<Test>());
});
}
}
@@ -0,0 +1,214 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Mock file for offences benchmarking.
#![cfg(test)]
use super::*;
use frame_support::parameter_types;
use frame_system as system;
use sp_runtime::{
SaturatedConversion,
traits::{IdentityLookup, Block as BlockT},
testing::{Header, UintAuthorityId},
};
type AccountId = u64;
type AccountIndex = u32;
type BlockNumber = u64;
type Balance = u64;
impl frame_system::Trait for Test {
type Origin = Origin;
type Index = AccountIndex;
type BlockNumber = BlockNumber;
type Call = Call;
type Hash = sp_core::H256;
type Hashing = ::sp_runtime::traits::BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = sp_runtime::testing::Header;
type Event = Event;
type BlockHashCount = ();
type MaximumBlockWeight = ();
type DbWeight = ();
type AvailableBlockRatio = ();
type MaximumBlockLength = ();
type Version = ();
type ModuleToIndex = ();
type AccountData = pallet_balances::AccountData<u64>;
type OnNewAccount = ();
type OnKilledAccount = (Balances,);
type BlockExecutionWeight = ();
type ExtrinsicBaseWeight = ();
}
parameter_types! {
pub const ExistentialDeposit: Balance = 10;
}
impl pallet_balances::Trait for Test {
type Balance = Balance;
type Event = Event;
type DustRemoval = ();
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
}
parameter_types! {
pub const MinimumPeriod: u64 = 5;
}
impl pallet_timestamp::Trait for Test {
type Moment = u64;
type OnTimestampSet = ();
type MinimumPeriod = MinimumPeriod;
}
impl pallet_session::historical::Trait for Test {
type FullIdentification = pallet_staking::Exposure<AccountId, Balance>;
type FullIdentificationOf = pallet_staking::ExposureOf<Test>;
}
sp_runtime::impl_opaque_keys! {
pub struct SessionKeys {
pub foo: sp_runtime::testing::UintAuthorityId,
}
}
pub struct TestSessionHandler;
impl pallet_session::SessionHandler<AccountId> for TestSessionHandler {
const KEY_TYPE_IDS: &'static [sp_runtime::KeyTypeId] = &[];
fn on_genesis_session<Ks: sp_runtime::traits::OpaqueKeys>(_validators: &[(AccountId, Ks)]) {}
fn on_new_session<Ks: sp_runtime::traits::OpaqueKeys>(
_: bool,
_: &[(AccountId, Ks)],
_: &[(AccountId, Ks)],
) {}
fn on_disabled(_: usize) {}
}
parameter_types! {
pub const Period: u64 = 1;
pub const Offset: u64 = 0;
}
impl pallet_session::Trait for Test {
type SessionManager = pallet_session::historical::NoteHistoricalRoot<Test, Staking>;
type Keys = SessionKeys;
type ShouldEndSession = pallet_session::PeriodicSessions<Period, Offset>;
type NextSessionRotation = pallet_session::PeriodicSessions<Period, Offset>;
type SessionHandler = TestSessionHandler;
type Event = Event;
type ValidatorId = AccountId;
type ValidatorIdOf = pallet_staking::StashOf<Test>;
type DisabledValidatorsThreshold = ();
}
pallet_staking_reward_curve::build! {
const I_NPOS: sp_runtime::curve::PiecewiseLinear<'static> = curve!(
min_inflation: 0_025_000,
max_inflation: 0_100_000,
ideal_stake: 0_500_000,
falloff: 0_050_000,
max_piece_count: 40,
test_precision: 0_005_000,
);
}
parameter_types! {
pub const RewardCurve: &'static sp_runtime::curve::PiecewiseLinear<'static> = &I_NPOS;
pub const MaxNominatorRewardedPerValidator: u32 = 64;
}
pub type Extrinsic = sp_runtime::testing::TestXt<Call, ()>;
pub struct CurrencyToVoteHandler;
impl Convert<u64, u64> for CurrencyToVoteHandler {
fn convert(x: u64) -> u64 {
x
}
}
impl Convert<u128, u64> for CurrencyToVoteHandler {
fn convert(x: u128) -> u64 {
x.saturated_into()
}
}
impl pallet_staking::Trait for Test {
type Currency = Balances;
type UnixTime = pallet_timestamp::Module<Self>;
type CurrencyToVote = CurrencyToVoteHandler;
type RewardRemainder = ();
type Event = Event;
type Slash = ();
type Reward = ();
type SessionsPerEra = ();
type SlashDeferDuration = ();
type SlashCancelOrigin = frame_system::EnsureRoot<Self::AccountId>;
type BondingDuration = ();
type SessionInterface = Self;
type RewardCurve = RewardCurve;
type NextNewSession = Session;
type ElectionLookahead = ();
type Call = Call;
type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator;
type UnsignedPriority = ();
type MaxIterations = ();
}
impl pallet_im_online::Trait for Test {
type AuthorityId = UintAuthorityId;
type Event = Event;
type SessionDuration = Period;
type ReportUnresponsiveness = Offences;
type UnsignedPriority = ();
}
impl pallet_offences::Trait for Test {
type Event = Event;
type IdentificationTuple = pallet_session::historical::IdentificationTuple<Self>;
type OnOffenceHandler = Staking;
}
impl<T> frame_system::offchain::SendTransactionTypes<T> for Test where Call: From<T> {
type Extrinsic = Extrinsic;
type OverarchingCall = Call;
}
impl crate::Trait for Test {}
pub type Block = sp_runtime::generic::Block<Header, UncheckedExtrinsic>;
pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic<u32, Call, u64, ()>;
frame_support::construct_runtime!(
pub enum Test where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: system::{Module, Call, Event<T>},
Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>},
Staking: pallet_staking::{Module, Call, Config<T>, Storage, Event<T>, ValidateUnsigned},
Session: pallet_session::{Module, Call, Storage, Event, Config<T>},
ImOnline: pallet_im_online::{Module, Call, Storage, Event<T>, ValidateUnsigned, Config<T>},
Offences: pallet_offences::{Module, Call, Storage, Event},
}
);
pub fn new_test_ext() -> sp_io::TestExternalities {
let t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
sp_io::TestExternalities::new(t)
}
+6
View File
@@ -2895,6 +2895,12 @@ impl<T: Trait> Module<T> {
pub fn put_election_status(status: ElectionStatus::<T::BlockNumber>) {
<EraElectionStatus<T>>::put(status);
}
#[cfg(feature = "runtime-benchmarks")]
pub fn set_slash_reward_fraction(fraction: Perbill) {
SlashRewardFraction::put(fraction);
}
}
/// In this implementation `new_session(session)` must be called before `end_session(session-1)`
@@ -26,8 +26,6 @@ use crate::SessionIndex;
/// The kind of an offence, is a byte string representing some kind identifier
/// e.g. `b"im-online:offlin"`, `b"babe:equivocatio"`
// TODO [slashing]: Is there something better we can have here that is more natural but still
// flexible? as you see in examples, they get cut off with long names.
pub type Kind = [u8; 16];
/// Number of times the offence of this authority was already reported in the past.