mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-21 17:55:42 +00:00
Benchmark Staking and Session Pallet (#5183)
* starting bench * More * more * Payout Validator * Give each validator exactly n nominators * Update with test * Try to add accounts to chain spec * Undo changes to chainspec * Payout nominator * Rebond and Reap Stash * Set history depth * fix smelly code * cancel deferred slash * new_era bench * do_slash benchmark * Add features * undo extrinsic move * lower * Update new era * Update benchmarking.rs * whitespace * Apply suggestions from code review Co-Authored-By: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * fixes * nit * Refactor tests, initial code * Move session benchmarks to avoid cyclic deps * Update lib.rs * Fix warnings * Move impl * Update to do random nominator allocation * add feature to benchmark pallet * Remove extra stuff * Update based on feedback * Less intrusive * Remove `transfer_idle_users` * remove again * unused dep * test feature flag * Update to latest substrate Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
Generated
+15
@@ -3543,6 +3543,7 @@ dependencies = [
|
|||||||
"pallet-randomness-collective-flip",
|
"pallet-randomness-collective-flip",
|
||||||
"pallet-recovery",
|
"pallet-recovery",
|
||||||
"pallet-session",
|
"pallet-session",
|
||||||
|
"pallet-session-benchmarking",
|
||||||
"pallet-society",
|
"pallet-society",
|
||||||
"pallet-staking",
|
"pallet-staking",
|
||||||
"pallet-staking-reward-curve",
|
"pallet-staking-reward-curve",
|
||||||
@@ -4396,6 +4397,18 @@ dependencies = [
|
|||||||
"sp-trie",
|
"sp-trie",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pallet-session-benchmarking"
|
||||||
|
version = "2.0.0-alpha.3"
|
||||||
|
dependencies = [
|
||||||
|
"frame-benchmarking",
|
||||||
|
"frame-system",
|
||||||
|
"pallet-session",
|
||||||
|
"pallet-staking",
|
||||||
|
"sp-runtime",
|
||||||
|
"sp-std",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pallet-society"
|
name = "pallet-society"
|
||||||
version = "2.0.0-alpha.3"
|
version = "2.0.0-alpha.3"
|
||||||
@@ -4416,6 +4429,7 @@ dependencies = [
|
|||||||
name = "pallet-staking"
|
name = "pallet-staking"
|
||||||
version = "2.0.0-alpha.3"
|
version = "2.0.0-alpha.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"frame-benchmarking",
|
||||||
"frame-support",
|
"frame-support",
|
||||||
"frame-system",
|
"frame-system",
|
||||||
"pallet-authorship",
|
"pallet-authorship",
|
||||||
@@ -4424,6 +4438,7 @@ dependencies = [
|
|||||||
"pallet-staking-reward-curve",
|
"pallet-staking-reward-curve",
|
||||||
"pallet-timestamp",
|
"pallet-timestamp",
|
||||||
"parity-scale-codec",
|
"parity-scale-codec",
|
||||||
|
"rand_chacha 0.2.1",
|
||||||
"serde",
|
"serde",
|
||||||
"sp-core",
|
"sp-core",
|
||||||
"sp-io",
|
"sp-io",
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ members = [
|
|||||||
"frame/recovery",
|
"frame/recovery",
|
||||||
"frame/scored-pool",
|
"frame/scored-pool",
|
||||||
"frame/session",
|
"frame/session",
|
||||||
|
"frame/session/benchmarking",
|
||||||
"frame/society",
|
"frame/society",
|
||||||
"frame/staking",
|
"frame/staking",
|
||||||
"frame/staking/reward-curve",
|
"frame/staking/reward-curve",
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ pallet-offences = { version = "2.0.0-alpha.2", default-features = false, path =
|
|||||||
pallet-randomness-collective-flip = { version = "2.0.0-alpha.2", default-features = false, path = "../../../frame/randomness-collective-flip" }
|
pallet-randomness-collective-flip = { version = "2.0.0-alpha.2", default-features = false, path = "../../../frame/randomness-collective-flip" }
|
||||||
pallet-recovery = { version = "2.0.0-alpha.2", default-features = false, path = "../../../frame/recovery" }
|
pallet-recovery = { version = "2.0.0-alpha.2", default-features = false, path = "../../../frame/recovery" }
|
||||||
pallet-session = { version = "2.0.0-alpha.2", features = ["historical"], path = "../../../frame/session", default-features = false }
|
pallet-session = { version = "2.0.0-alpha.2", features = ["historical"], path = "../../../frame/session", default-features = false }
|
||||||
|
pallet-session-benchmarking = { version = "2.0.0-alpha.2", path = "../../../frame/session/benchmarking", default-features = false, optional = true }
|
||||||
pallet-staking = { version = "2.0.0-alpha.2", features = ["migrate"], path = "../../../frame/staking", default-features = false }
|
pallet-staking = { version = "2.0.0-alpha.2", features = ["migrate"], path = "../../../frame/staking", default-features = false }
|
||||||
pallet-staking-reward-curve = { version = "2.0.0-alpha.2", path = "../../../frame/staking/reward-curve" }
|
pallet-staking-reward-curve = { version = "2.0.0-alpha.2", path = "../../../frame/staking/reward-curve" }
|
||||||
pallet-sudo = { version = "2.0.0-alpha.2", default-features = false, path = "../../../frame/sudo" }
|
pallet-sudo = { version = "2.0.0-alpha.2", default-features = false, path = "../../../frame/sudo" }
|
||||||
@@ -134,8 +135,10 @@ std = [
|
|||||||
]
|
]
|
||||||
runtime-benchmarks = [
|
runtime-benchmarks = [
|
||||||
"frame-benchmarking",
|
"frame-benchmarking",
|
||||||
"pallet-timestamp/runtime-benchmarks",
|
|
||||||
"pallet-identity/runtime-benchmarks",
|
|
||||||
"pallet-balances/runtime-benchmarks",
|
"pallet-balances/runtime-benchmarks",
|
||||||
|
"pallet-identity/runtime-benchmarks",
|
||||||
|
"pallet-session-benchmarking",
|
||||||
|
"pallet-timestamp/runtime-benchmarks",
|
||||||
"pallet-vesting/runtime-benchmarks",
|
"pallet-vesting/runtime-benchmarks",
|
||||||
|
"pallet-staking/runtime-benchmarks",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -851,6 +851,11 @@ impl_runtime_apis! {
|
|||||||
repeat: u32,
|
repeat: u32,
|
||||||
) -> Result<Vec<frame_benchmarking::BenchmarkResults>, sp_runtime::RuntimeString> {
|
) -> Result<Vec<frame_benchmarking::BenchmarkResults>, sp_runtime::RuntimeString> {
|
||||||
use frame_benchmarking::Benchmarking;
|
use frame_benchmarking::Benchmarking;
|
||||||
|
// Trying to add benchmarks directly to the Session Pallet caused cyclic dependency issues.
|
||||||
|
// To get around that, we separated the Session benchmarks into its own crate, which is why
|
||||||
|
// we need these two lines below.
|
||||||
|
use pallet_session_benchmarking::Module as SessionBench;
|
||||||
|
impl pallet_session_benchmarking::Trait for Runtime {}
|
||||||
|
|
||||||
let result = match module.as_slice() {
|
let result = match module.as_slice() {
|
||||||
b"pallet-balances" | b"balances" => Balances::run_benchmark(
|
b"pallet-balances" | b"balances" => Balances::run_benchmark(
|
||||||
@@ -867,6 +872,20 @@ impl_runtime_apis! {
|
|||||||
steps,
|
steps,
|
||||||
repeat,
|
repeat,
|
||||||
),
|
),
|
||||||
|
b"pallet-session" | b"session" => SessionBench::<Runtime>::run_benchmark(
|
||||||
|
extrinsic,
|
||||||
|
lowest_range_values,
|
||||||
|
highest_range_values,
|
||||||
|
steps,
|
||||||
|
repeat,
|
||||||
|
),
|
||||||
|
b"pallet-staking" | b"staking" => Staking::run_benchmark(
|
||||||
|
extrinsic,
|
||||||
|
lowest_range_values,
|
||||||
|
highest_range_values,
|
||||||
|
steps,
|
||||||
|
repeat,
|
||||||
|
),
|
||||||
b"pallet-timestamp" | b"timestamp" => Timestamp::run_benchmark(
|
b"pallet-timestamp" | b"timestamp" => Timestamp::run_benchmark(
|
||||||
extrinsic,
|
extrinsic,
|
||||||
lowest_range_values,
|
lowest_range_values,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ sp-io = { version = "2.0.0-alpha.3", default-features = false, path = "../../pri
|
|||||||
sp-runtime = { version = "2.0.0-alpha.3", default-features = false, path = "../../primitives/runtime" }
|
sp-runtime = { version = "2.0.0-alpha.3", default-features = false, path = "../../primitives/runtime" }
|
||||||
frame-support = { version = "2.0.0-alpha.3", default-features = false, path = "../support" }
|
frame-support = { version = "2.0.0-alpha.3", default-features = false, path = "../support" }
|
||||||
frame-system = { version = "2.0.0-alpha.3", default-features = false, path = "../system" }
|
frame-system = { version = "2.0.0-alpha.3", default-features = false, path = "../system" }
|
||||||
frame-benchmarking = { version = "2.0.0-alpha.3", default-features = false, path = "../benchmarking" }
|
frame-benchmarking = { version = "2.0.0-alpha.3", default-features = false, path = "../benchmarking", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
@@ -27,3 +27,4 @@ std = [
|
|||||||
"frame-system/std",
|
"frame-system/std",
|
||||||
"frame-benchmarking/std",
|
"frame-benchmarking/std",
|
||||||
]
|
]
|
||||||
|
runtime-benchmarks = ["frame-benchmarking"]
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ use frame_system::{self as system, ensure_signed};
|
|||||||
use codec::{Encode, Decode};
|
use codec::{Encode, Decode};
|
||||||
use sp_std::prelude::Vec;
|
use sp_std::prelude::Vec;
|
||||||
|
|
||||||
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
pub mod benchmarking;
|
pub mod benchmarking;
|
||||||
|
|
||||||
/// Type alias for currency balance.
|
/// Type alias for currency balance.
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ sp-staking = { version = "2.0.0-alpha.2", default-features = false, path = "../.
|
|||||||
frame-support = { version = "2.0.0-alpha.2", default-features = false, path = "../support" }
|
frame-support = { version = "2.0.0-alpha.2", default-features = false, path = "../support" }
|
||||||
frame-system = { version = "2.0.0-alpha.2", default-features = false, path = "../system" }
|
frame-system = { version = "2.0.0-alpha.2", default-features = false, path = "../system" }
|
||||||
pallet-timestamp = { version = "2.0.0-alpha.2", default-features = false, path = "../timestamp" }
|
pallet-timestamp = { version = "2.0.0-alpha.2", default-features = false, path = "../timestamp" }
|
||||||
sp-trie = { optional = true, path = "../../primitives/trie", default-features = false , version = "2.0.0-alpha.2"}
|
sp-trie = { optional = true, path = "../../primitives/trie", default-features = false, version = "2.0.0-alpha.2"}
|
||||||
sp-io ={ path = "../../primitives/io", default-features = false , version = "2.0.0-alpha.2"}
|
sp-io ={ path = "../../primitives/io", default-features = false , version = "2.0.0-alpha.2"}
|
||||||
impl-trait-for-tuples = "0.1.3"
|
impl-trait-for-tuples = "0.1.3"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
[package]
|
||||||
|
name = "pallet-session-benchmarking"
|
||||||
|
version = "2.0.0-alpha.3"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
license = "GPL-3.0"
|
||||||
|
homepage = "https://substrate.dev"
|
||||||
|
repository = "https://github.com/paritytech/substrate/"
|
||||||
|
description = "FRAME sessions pallet benchmarking"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
sp-std = { version = "2.0.0-alpha.2", default-features = false, path = "../../../primitives/std" }
|
||||||
|
sp-runtime = { version = "2.0.0-alpha.2", default-features = false, path = "../../../primitives/runtime" }
|
||||||
|
frame-system = { version = "2.0.0-alpha.2", default-features = false, path = "../../system" }
|
||||||
|
frame-benchmarking = { version = "2.0.0-alpha.2", default-features = false, path = "../../benchmarking" }
|
||||||
|
pallet-staking = { version = "2.0.0-alpha.2", default-features = false, features = ["runtime-benchmarks"], path = "../../staking" }
|
||||||
|
pallet-session = { version = "2.0.0-alpha.2", default-features = false, path = "../../session" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = [
|
||||||
|
"sp-std/std",
|
||||||
|
"sp-runtime/std",
|
||||||
|
"frame-system/std",
|
||||||
|
"frame-benchmarking/std",
|
||||||
|
"pallet-staking/std",
|
||||||
|
"pallet-session/std",
|
||||||
|
]
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Benchmarks for the Session Pallet.
|
||||||
|
// This is separated into its own crate due to cyclic dependency issues.
|
||||||
|
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
use sp_std::prelude::*;
|
||||||
|
use sp_std::vec;
|
||||||
|
|
||||||
|
use frame_system::RawOrigin;
|
||||||
|
use frame_benchmarking::benchmarks;
|
||||||
|
|
||||||
|
use pallet_session::*;
|
||||||
|
use pallet_session::Module as Session;
|
||||||
|
|
||||||
|
use pallet_staking::{
|
||||||
|
MAX_NOMINATIONS,
|
||||||
|
benchmarking::create_validator_with_nominators,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Module<T: Trait>(pallet_session::Module<T>);
|
||||||
|
|
||||||
|
pub trait Trait: pallet_session::Trait + pallet_staking::Trait {}
|
||||||
|
|
||||||
|
benchmarks! {
|
||||||
|
_ { }
|
||||||
|
|
||||||
|
set_keys {
|
||||||
|
let n in 1 .. MAX_NOMINATIONS as u32;
|
||||||
|
let validator = create_validator_with_nominators::<T>(n, MAX_NOMINATIONS as u32)?;
|
||||||
|
let keys = T::Keys::default();
|
||||||
|
let proof: Vec<u8> = vec![0,1,2,3];
|
||||||
|
}: _(RawOrigin::Signed(validator), keys, proof)
|
||||||
|
|
||||||
|
purge_keys {
|
||||||
|
let n in 1 .. MAX_NOMINATIONS as u32;
|
||||||
|
let validator = create_validator_with_nominators::<T>(n, MAX_NOMINATIONS as u32)?;
|
||||||
|
let keys = T::Keys::default();
|
||||||
|
let proof: Vec<u8> = vec![0,1,2,3];
|
||||||
|
Session::<T>::set_keys(RawOrigin::Signed(validator.clone()).into(), keys, proof)?;
|
||||||
|
}: _(RawOrigin::Signed(validator))
|
||||||
|
}
|
||||||
@@ -113,6 +113,8 @@ use frame_support::traits::MigrateAccount;
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod mock;
|
mod mock;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
#[cfg(feature = "historical")]
|
#[cfg(feature = "historical")]
|
||||||
pub mod historical;
|
pub mod historical;
|
||||||
@@ -466,7 +468,7 @@ decl_module! {
|
|||||||
/// In this case, purge_keys will need to be called before the account can be removed.
|
/// In this case, purge_keys will need to be called before the account can be removed.
|
||||||
/// # </weight>
|
/// # </weight>
|
||||||
#[weight = SimpleDispatchInfo::FixedNormal(150_000)]
|
#[weight = SimpleDispatchInfo::FixedNormal(150_000)]
|
||||||
fn set_keys(origin, keys: T::Keys, proof: Vec<u8>) -> dispatch::DispatchResult {
|
pub fn set_keys(origin, keys: T::Keys, proof: Vec<u8>) -> dispatch::DispatchResult {
|
||||||
let who = ensure_signed(origin)?;
|
let who = ensure_signed(origin)?;
|
||||||
|
|
||||||
ensure!(keys.ownership_proof_is_valid(&proof), Error::<T>::InvalidProof);
|
ensure!(keys.ownership_proof_is_valid(&proof), Error::<T>::InvalidProof);
|
||||||
@@ -487,7 +489,7 @@ decl_module! {
|
|||||||
/// - Reduces system account refs by one on success.
|
/// - Reduces system account refs by one on success.
|
||||||
/// # </weight>
|
/// # </weight>
|
||||||
#[weight = SimpleDispatchInfo::FixedNormal(150_000)]
|
#[weight = SimpleDispatchInfo::FixedNormal(150_000)]
|
||||||
fn purge_keys(origin) {
|
pub fn purge_keys(origin) {
|
||||||
let who = ensure_signed(origin)?;
|
let who = ensure_signed(origin)?;
|
||||||
Self::do_purge_keys(&who)?;
|
Self::do_purge_keys(&who)?;
|
||||||
}
|
}
|
||||||
@@ -742,308 +744,3 @@ impl<T: Trait, Inner: FindAuthor<u32>> FindAuthor<T::ValidatorId>
|
|||||||
validators.get(i as usize).map(|k| k.clone())
|
validators.get(i as usize).map(|k| k.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use frame_support::assert_ok;
|
|
||||||
use sp_core::crypto::key_types::DUMMY;
|
|
||||||
use sp_runtime::{traits::OnInitialize, testing::UintAuthorityId};
|
|
||||||
use mock::{
|
|
||||||
NEXT_VALIDATORS, SESSION_CHANGED, TEST_SESSION_CHANGED, authorities, force_new_session,
|
|
||||||
set_next_validators, set_session_length, session_changed, Test, Origin, System, Session,
|
|
||||||
reset_before_session_end_called, before_session_end_called,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn new_test_ext() -> sp_io::TestExternalities {
|
|
||||||
let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
|
||||||
GenesisConfig::<Test> {
|
|
||||||
keys: NEXT_VALIDATORS.with(|l|
|
|
||||||
l.borrow().iter().cloned().map(|i| (i, i, UintAuthorityId(i).into())).collect()
|
|
||||||
),
|
|
||||||
}.assimilate_storage(&mut t).unwrap();
|
|
||||||
sp_io::TestExternalities::new(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn initialize_block(block: u64) {
|
|
||||||
SESSION_CHANGED.with(|l| *l.borrow_mut() = false);
|
|
||||||
System::set_block_number(block);
|
|
||||||
Session::on_initialize(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simple_setup_should_work() {
|
|
||||||
new_test_ext().execute_with(|| {
|
|
||||||
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
|
|
||||||
assert_eq!(Session::validators(), vec![1, 2, 3]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn put_get_keys() {
|
|
||||||
new_test_ext().execute_with(|| {
|
|
||||||
Session::put_keys(&10, &UintAuthorityId(10).into());
|
|
||||||
assert_eq!(Session::load_keys(&10), Some(UintAuthorityId(10).into()));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn keys_cleared_on_kill() {
|
|
||||||
let mut ext = new_test_ext();
|
|
||||||
ext.execute_with(|| {
|
|
||||||
assert_eq!(Session::validators(), vec![1, 2, 3]);
|
|
||||||
assert_eq!(Session::load_keys(&1), Some(UintAuthorityId(1).into()));
|
|
||||||
|
|
||||||
let id = DUMMY;
|
|
||||||
assert_eq!(Session::key_owner(id, UintAuthorityId(1).get_raw(id)), Some(1));
|
|
||||||
|
|
||||||
assert!(!System::allow_death(&1));
|
|
||||||
assert_ok!(Session::purge_keys(Origin::signed(1)));
|
|
||||||
assert!(System::allow_death(&1));
|
|
||||||
|
|
||||||
assert_eq!(Session::load_keys(&1), None);
|
|
||||||
assert_eq!(Session::key_owner(id, UintAuthorityId(1).get_raw(id)), None);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn authorities_should_track_validators() {
|
|
||||||
reset_before_session_end_called();
|
|
||||||
|
|
||||||
new_test_ext().execute_with(|| {
|
|
||||||
set_next_validators(vec![1, 2]);
|
|
||||||
force_new_session();
|
|
||||||
initialize_block(1);
|
|
||||||
assert_eq!(Session::queued_keys(), vec![
|
|
||||||
(1, UintAuthorityId(1).into()),
|
|
||||||
(2, UintAuthorityId(2).into()),
|
|
||||||
]);
|
|
||||||
assert_eq!(Session::validators(), vec![1, 2, 3]);
|
|
||||||
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
|
|
||||||
assert!(before_session_end_called());
|
|
||||||
reset_before_session_end_called();
|
|
||||||
|
|
||||||
force_new_session();
|
|
||||||
initialize_block(2);
|
|
||||||
assert_eq!(Session::queued_keys(), vec![
|
|
||||||
(1, UintAuthorityId(1).into()),
|
|
||||||
(2, UintAuthorityId(2).into()),
|
|
||||||
]);
|
|
||||||
assert_eq!(Session::validators(), vec![1, 2]);
|
|
||||||
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2)]);
|
|
||||||
assert!(before_session_end_called());
|
|
||||||
reset_before_session_end_called();
|
|
||||||
|
|
||||||
set_next_validators(vec![1, 2, 4]);
|
|
||||||
assert_ok!(Session::set_keys(Origin::signed(4), UintAuthorityId(4).into(), vec![]));
|
|
||||||
force_new_session();
|
|
||||||
initialize_block(3);
|
|
||||||
assert_eq!(Session::queued_keys(), vec![
|
|
||||||
(1, UintAuthorityId(1).into()),
|
|
||||||
(2, UintAuthorityId(2).into()),
|
|
||||||
(4, UintAuthorityId(4).into()),
|
|
||||||
]);
|
|
||||||
assert_eq!(Session::validators(), vec![1, 2]);
|
|
||||||
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2)]);
|
|
||||||
assert!(before_session_end_called());
|
|
||||||
|
|
||||||
force_new_session();
|
|
||||||
initialize_block(4);
|
|
||||||
assert_eq!(Session::queued_keys(), vec![
|
|
||||||
(1, UintAuthorityId(1).into()),
|
|
||||||
(2, UintAuthorityId(2).into()),
|
|
||||||
(4, UintAuthorityId(4).into()),
|
|
||||||
]);
|
|
||||||
assert_eq!(Session::validators(), vec![1, 2, 4]);
|
|
||||||
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(4)]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_work_with_early_exit() {
|
|
||||||
new_test_ext().execute_with(|| {
|
|
||||||
set_session_length(10);
|
|
||||||
|
|
||||||
initialize_block(1);
|
|
||||||
assert_eq!(Session::current_index(), 0);
|
|
||||||
|
|
||||||
initialize_block(2);
|
|
||||||
assert_eq!(Session::current_index(), 0);
|
|
||||||
|
|
||||||
force_new_session();
|
|
||||||
initialize_block(3);
|
|
||||||
assert_eq!(Session::current_index(), 1);
|
|
||||||
|
|
||||||
initialize_block(9);
|
|
||||||
assert_eq!(Session::current_index(), 1);
|
|
||||||
|
|
||||||
initialize_block(10);
|
|
||||||
assert_eq!(Session::current_index(), 2);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn session_change_should_work() {
|
|
||||||
new_test_ext().execute_with(|| {
|
|
||||||
// Block 1: No change
|
|
||||||
initialize_block(1);
|
|
||||||
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
|
|
||||||
|
|
||||||
// Block 2: Session rollover, but no change.
|
|
||||||
initialize_block(2);
|
|
||||||
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
|
|
||||||
|
|
||||||
// Block 3: Set new key for validator 2; no visible change.
|
|
||||||
initialize_block(3);
|
|
||||||
assert_ok!(Session::set_keys(Origin::signed(2), UintAuthorityId(5).into(), vec![]));
|
|
||||||
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
|
|
||||||
|
|
||||||
// Block 4: Session rollover; no visible change.
|
|
||||||
initialize_block(4);
|
|
||||||
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
|
|
||||||
|
|
||||||
// Block 5: No change.
|
|
||||||
initialize_block(5);
|
|
||||||
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
|
|
||||||
|
|
||||||
// Block 6: Session rollover; authority 2 changes.
|
|
||||||
initialize_block(6);
|
|
||||||
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(5), UintAuthorityId(3)]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn duplicates_are_not_allowed() {
|
|
||||||
new_test_ext().execute_with(|| {
|
|
||||||
System::set_block_number(1);
|
|
||||||
Session::on_initialize(1);
|
|
||||||
assert!(Session::set_keys(Origin::signed(4), UintAuthorityId(1).into(), vec![]).is_err());
|
|
||||||
assert!(Session::set_keys(Origin::signed(1), UintAuthorityId(10).into(), vec![]).is_ok());
|
|
||||||
|
|
||||||
// is fine now that 1 has migrated off.
|
|
||||||
assert!(Session::set_keys(Origin::signed(4), UintAuthorityId(1).into(), vec![]).is_ok());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn session_changed_flag_works() {
|
|
||||||
reset_before_session_end_called();
|
|
||||||
|
|
||||||
new_test_ext().execute_with(|| {
|
|
||||||
TEST_SESSION_CHANGED.with(|l| *l.borrow_mut() = true);
|
|
||||||
|
|
||||||
force_new_session();
|
|
||||||
initialize_block(1);
|
|
||||||
assert!(!session_changed());
|
|
||||||
assert!(before_session_end_called());
|
|
||||||
reset_before_session_end_called();
|
|
||||||
|
|
||||||
force_new_session();
|
|
||||||
initialize_block(2);
|
|
||||||
assert!(!session_changed());
|
|
||||||
assert!(before_session_end_called());
|
|
||||||
reset_before_session_end_called();
|
|
||||||
|
|
||||||
Session::disable_index(0);
|
|
||||||
force_new_session();
|
|
||||||
initialize_block(3);
|
|
||||||
assert!(!session_changed());
|
|
||||||
assert!(before_session_end_called());
|
|
||||||
reset_before_session_end_called();
|
|
||||||
|
|
||||||
force_new_session();
|
|
||||||
initialize_block(4);
|
|
||||||
assert!(session_changed());
|
|
||||||
assert!(before_session_end_called());
|
|
||||||
reset_before_session_end_called();
|
|
||||||
|
|
||||||
force_new_session();
|
|
||||||
initialize_block(5);
|
|
||||||
assert!(!session_changed());
|
|
||||||
assert!(before_session_end_called());
|
|
||||||
reset_before_session_end_called();
|
|
||||||
|
|
||||||
assert_ok!(Session::set_keys(Origin::signed(2), UintAuthorityId(5).into(), vec![]));
|
|
||||||
force_new_session();
|
|
||||||
initialize_block(6);
|
|
||||||
assert!(!session_changed());
|
|
||||||
assert!(before_session_end_called());
|
|
||||||
reset_before_session_end_called();
|
|
||||||
|
|
||||||
// changing the keys of a validator leads to change.
|
|
||||||
assert_ok!(Session::set_keys(Origin::signed(69), UintAuthorityId(69).into(), vec![]));
|
|
||||||
force_new_session();
|
|
||||||
initialize_block(7);
|
|
||||||
assert!(session_changed());
|
|
||||||
assert!(before_session_end_called());
|
|
||||||
reset_before_session_end_called();
|
|
||||||
|
|
||||||
// while changing the keys of a non-validator does not.
|
|
||||||
force_new_session();
|
|
||||||
initialize_block(7);
|
|
||||||
assert!(!session_changed());
|
|
||||||
assert!(before_session_end_called());
|
|
||||||
reset_before_session_end_called();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn periodic_session_works() {
|
|
||||||
struct Period;
|
|
||||||
struct Offset;
|
|
||||||
|
|
||||||
impl Get<u64> for Period {
|
|
||||||
fn get() -> u64 { 10 }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Get<u64> for Offset {
|
|
||||||
fn get() -> u64 { 3 }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type P = PeriodicSessions<Period, Offset>;
|
|
||||||
|
|
||||||
for i in 0..3 {
|
|
||||||
assert!(!P::should_end_session(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(P::should_end_session(3));
|
|
||||||
|
|
||||||
for i in (1..10).map(|i| 3 + i) {
|
|
||||||
assert!(!P::should_end_session(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(P::should_end_session(13));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn session_keys_generate_output_works_as_set_keys_input() {
|
|
||||||
new_test_ext().execute_with(|| {
|
|
||||||
let new_keys = mock::MockSessionKeys::generate(None);
|
|
||||||
assert_ok!(
|
|
||||||
Session::set_keys(
|
|
||||||
Origin::signed(2),
|
|
||||||
<mock::Test as Trait>::Keys::decode(&mut &new_keys[..]).expect("Decode keys"),
|
|
||||||
vec![],
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_true_if_more_than_third_is_disabled() {
|
|
||||||
new_test_ext().execute_with(|| {
|
|
||||||
set_next_validators(vec![1, 2, 3, 4, 5, 6, 7]);
|
|
||||||
force_new_session();
|
|
||||||
initialize_block(1);
|
|
||||||
// apply the new validator set
|
|
||||||
force_new_session();
|
|
||||||
initialize_block(2);
|
|
||||||
|
|
||||||
assert_eq!(Session::disable_index(0), false);
|
|
||||||
assert_eq!(Session::disable_index(1), false);
|
|
||||||
assert_eq!(Session::disable_index(2), true);
|
|
||||||
assert_eq!(Session::disable_index(3), true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,319 @@
|
|||||||
|
// Copyright 2017-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/>.
|
||||||
|
|
||||||
|
// Tests for the Session Pallet
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use frame_support::assert_ok;
|
||||||
|
use sp_core::crypto::key_types::DUMMY;
|
||||||
|
use sp_runtime::{traits::OnInitialize, testing::UintAuthorityId};
|
||||||
|
use mock::{
|
||||||
|
NEXT_VALIDATORS, SESSION_CHANGED, TEST_SESSION_CHANGED, authorities, force_new_session,
|
||||||
|
set_next_validators, set_session_length, session_changed, Test, Origin, System, Session,
|
||||||
|
reset_before_session_end_called, before_session_end_called,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn new_test_ext() -> sp_io::TestExternalities {
|
||||||
|
let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||||
|
GenesisConfig::<Test> {
|
||||||
|
keys: NEXT_VALIDATORS.with(|l|
|
||||||
|
l.borrow().iter().cloned().map(|i| (i, i, UintAuthorityId(i).into())).collect()
|
||||||
|
),
|
||||||
|
}.assimilate_storage(&mut t).unwrap();
|
||||||
|
sp_io::TestExternalities::new(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialize_block(block: u64) {
|
||||||
|
SESSION_CHANGED.with(|l| *l.borrow_mut() = false);
|
||||||
|
System::set_block_number(block);
|
||||||
|
Session::on_initialize(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_setup_should_work() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
|
||||||
|
assert_eq!(Session::validators(), vec![1, 2, 3]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn put_get_keys() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
Session::put_keys(&10, &UintAuthorityId(10).into());
|
||||||
|
assert_eq!(Session::load_keys(&10), Some(UintAuthorityId(10).into()));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn keys_cleared_on_kill() {
|
||||||
|
let mut ext = new_test_ext();
|
||||||
|
ext.execute_with(|| {
|
||||||
|
assert_eq!(Session::validators(), vec![1, 2, 3]);
|
||||||
|
assert_eq!(Session::load_keys(&1), Some(UintAuthorityId(1).into()));
|
||||||
|
|
||||||
|
let id = DUMMY;
|
||||||
|
assert_eq!(Session::key_owner(id, UintAuthorityId(1).get_raw(id)), Some(1));
|
||||||
|
|
||||||
|
assert!(!System::allow_death(&1));
|
||||||
|
assert_ok!(Session::purge_keys(Origin::signed(1)));
|
||||||
|
assert!(System::allow_death(&1));
|
||||||
|
|
||||||
|
assert_eq!(Session::load_keys(&1), None);
|
||||||
|
assert_eq!(Session::key_owner(id, UintAuthorityId(1).get_raw(id)), None);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn authorities_should_track_validators() {
|
||||||
|
reset_before_session_end_called();
|
||||||
|
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
set_next_validators(vec![1, 2]);
|
||||||
|
force_new_session();
|
||||||
|
initialize_block(1);
|
||||||
|
assert_eq!(Session::queued_keys(), vec![
|
||||||
|
(1, UintAuthorityId(1).into()),
|
||||||
|
(2, UintAuthorityId(2).into()),
|
||||||
|
]);
|
||||||
|
assert_eq!(Session::validators(), vec![1, 2, 3]);
|
||||||
|
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
|
||||||
|
assert!(before_session_end_called());
|
||||||
|
reset_before_session_end_called();
|
||||||
|
|
||||||
|
force_new_session();
|
||||||
|
initialize_block(2);
|
||||||
|
assert_eq!(Session::queued_keys(), vec![
|
||||||
|
(1, UintAuthorityId(1).into()),
|
||||||
|
(2, UintAuthorityId(2).into()),
|
||||||
|
]);
|
||||||
|
assert_eq!(Session::validators(), vec![1, 2]);
|
||||||
|
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2)]);
|
||||||
|
assert!(before_session_end_called());
|
||||||
|
reset_before_session_end_called();
|
||||||
|
|
||||||
|
set_next_validators(vec![1, 2, 4]);
|
||||||
|
assert_ok!(Session::set_keys(Origin::signed(4), UintAuthorityId(4).into(), vec![]));
|
||||||
|
force_new_session();
|
||||||
|
initialize_block(3);
|
||||||
|
assert_eq!(Session::queued_keys(), vec![
|
||||||
|
(1, UintAuthorityId(1).into()),
|
||||||
|
(2, UintAuthorityId(2).into()),
|
||||||
|
(4, UintAuthorityId(4).into()),
|
||||||
|
]);
|
||||||
|
assert_eq!(Session::validators(), vec![1, 2]);
|
||||||
|
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2)]);
|
||||||
|
assert!(before_session_end_called());
|
||||||
|
|
||||||
|
force_new_session();
|
||||||
|
initialize_block(4);
|
||||||
|
assert_eq!(Session::queued_keys(), vec![
|
||||||
|
(1, UintAuthorityId(1).into()),
|
||||||
|
(2, UintAuthorityId(2).into()),
|
||||||
|
(4, UintAuthorityId(4).into()),
|
||||||
|
]);
|
||||||
|
assert_eq!(Session::validators(), vec![1, 2, 4]);
|
||||||
|
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(4)]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_work_with_early_exit() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
set_session_length(10);
|
||||||
|
|
||||||
|
initialize_block(1);
|
||||||
|
assert_eq!(Session::current_index(), 0);
|
||||||
|
|
||||||
|
initialize_block(2);
|
||||||
|
assert_eq!(Session::current_index(), 0);
|
||||||
|
|
||||||
|
force_new_session();
|
||||||
|
initialize_block(3);
|
||||||
|
assert_eq!(Session::current_index(), 1);
|
||||||
|
|
||||||
|
initialize_block(9);
|
||||||
|
assert_eq!(Session::current_index(), 1);
|
||||||
|
|
||||||
|
initialize_block(10);
|
||||||
|
assert_eq!(Session::current_index(), 2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn session_change_should_work() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
// Block 1: No change
|
||||||
|
initialize_block(1);
|
||||||
|
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
|
||||||
|
|
||||||
|
// Block 2: Session rollover, but no change.
|
||||||
|
initialize_block(2);
|
||||||
|
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
|
||||||
|
|
||||||
|
// Block 3: Set new key for validator 2; no visible change.
|
||||||
|
initialize_block(3);
|
||||||
|
assert_ok!(Session::set_keys(Origin::signed(2), UintAuthorityId(5).into(), vec![]));
|
||||||
|
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
|
||||||
|
|
||||||
|
// Block 4: Session rollover; no visible change.
|
||||||
|
initialize_block(4);
|
||||||
|
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
|
||||||
|
|
||||||
|
// Block 5: No change.
|
||||||
|
initialize_block(5);
|
||||||
|
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
|
||||||
|
|
||||||
|
// Block 6: Session rollover; authority 2 changes.
|
||||||
|
initialize_block(6);
|
||||||
|
assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(5), UintAuthorityId(3)]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn duplicates_are_not_allowed() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
System::set_block_number(1);
|
||||||
|
Session::on_initialize(1);
|
||||||
|
assert!(Session::set_keys(Origin::signed(4), UintAuthorityId(1).into(), vec![]).is_err());
|
||||||
|
assert!(Session::set_keys(Origin::signed(1), UintAuthorityId(10).into(), vec![]).is_ok());
|
||||||
|
|
||||||
|
// is fine now that 1 has migrated off.
|
||||||
|
assert!(Session::set_keys(Origin::signed(4), UintAuthorityId(1).into(), vec![]).is_ok());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn session_changed_flag_works() {
|
||||||
|
reset_before_session_end_called();
|
||||||
|
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
TEST_SESSION_CHANGED.with(|l| *l.borrow_mut() = true);
|
||||||
|
|
||||||
|
force_new_session();
|
||||||
|
initialize_block(1);
|
||||||
|
assert!(!session_changed());
|
||||||
|
assert!(before_session_end_called());
|
||||||
|
reset_before_session_end_called();
|
||||||
|
|
||||||
|
force_new_session();
|
||||||
|
initialize_block(2);
|
||||||
|
assert!(!session_changed());
|
||||||
|
assert!(before_session_end_called());
|
||||||
|
reset_before_session_end_called();
|
||||||
|
|
||||||
|
Session::disable_index(0);
|
||||||
|
force_new_session();
|
||||||
|
initialize_block(3);
|
||||||
|
assert!(!session_changed());
|
||||||
|
assert!(before_session_end_called());
|
||||||
|
reset_before_session_end_called();
|
||||||
|
|
||||||
|
force_new_session();
|
||||||
|
initialize_block(4);
|
||||||
|
assert!(session_changed());
|
||||||
|
assert!(before_session_end_called());
|
||||||
|
reset_before_session_end_called();
|
||||||
|
|
||||||
|
force_new_session();
|
||||||
|
initialize_block(5);
|
||||||
|
assert!(!session_changed());
|
||||||
|
assert!(before_session_end_called());
|
||||||
|
reset_before_session_end_called();
|
||||||
|
|
||||||
|
assert_ok!(Session::set_keys(Origin::signed(2), UintAuthorityId(5).into(), vec![]));
|
||||||
|
force_new_session();
|
||||||
|
initialize_block(6);
|
||||||
|
assert!(!session_changed());
|
||||||
|
assert!(before_session_end_called());
|
||||||
|
reset_before_session_end_called();
|
||||||
|
|
||||||
|
// changing the keys of a validator leads to change.
|
||||||
|
assert_ok!(Session::set_keys(Origin::signed(69), UintAuthorityId(69).into(), vec![]));
|
||||||
|
force_new_session();
|
||||||
|
initialize_block(7);
|
||||||
|
assert!(session_changed());
|
||||||
|
assert!(before_session_end_called());
|
||||||
|
reset_before_session_end_called();
|
||||||
|
|
||||||
|
// while changing the keys of a non-validator does not.
|
||||||
|
force_new_session();
|
||||||
|
initialize_block(7);
|
||||||
|
assert!(!session_changed());
|
||||||
|
assert!(before_session_end_called());
|
||||||
|
reset_before_session_end_called();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn periodic_session_works() {
|
||||||
|
struct Period;
|
||||||
|
struct Offset;
|
||||||
|
|
||||||
|
impl Get<u64> for Period {
|
||||||
|
fn get() -> u64 { 10 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Get<u64> for Offset {
|
||||||
|
fn get() -> u64 { 3 }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type P = PeriodicSessions<Period, Offset>;
|
||||||
|
|
||||||
|
for i in 0..3 {
|
||||||
|
assert!(!P::should_end_session(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(P::should_end_session(3));
|
||||||
|
|
||||||
|
for i in (1..10).map(|i| 3 + i) {
|
||||||
|
assert!(!P::should_end_session(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(P::should_end_session(13));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn session_keys_generate_output_works_as_set_keys_input() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let new_keys = mock::MockSessionKeys::generate(None);
|
||||||
|
assert_ok!(
|
||||||
|
Session::set_keys(
|
||||||
|
Origin::signed(2),
|
||||||
|
<mock::Test as Trait>::Keys::decode(&mut &new_keys[..]).expect("Decode keys"),
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn return_true_if_more_than_third_is_disabled() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
set_next_validators(vec![1, 2, 3, 4, 5, 6, 7]);
|
||||||
|
force_new_session();
|
||||||
|
initialize_block(1);
|
||||||
|
// apply the new validator set
|
||||||
|
force_new_session();
|
||||||
|
initialize_block(2);
|
||||||
|
|
||||||
|
assert_eq!(Session::disable_index(0), false);
|
||||||
|
assert_eq!(Session::disable_index(1), false);
|
||||||
|
assert_eq!(Session::disable_index(2), true);
|
||||||
|
assert_eq!(Session::disable_index(3), true);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -22,12 +22,17 @@ frame-system = { version = "2.0.0-alpha.2", default-features = false, path = "..
|
|||||||
pallet-session = { version = "2.0.0-alpha.2", features = ["historical"], path = "../session", default-features = false }
|
pallet-session = { version = "2.0.0-alpha.2", features = ["historical"], path = "../session", default-features = false }
|
||||||
pallet-authorship = { version = "2.0.0-alpha.2", default-features = false, path = "../authorship" }
|
pallet-authorship = { version = "2.0.0-alpha.2", default-features = false, path = "../authorship" }
|
||||||
|
|
||||||
|
frame-benchmarking = { version = "2.0.0-alpha.2", default-features = false, path = "../benchmarking", optional = true }
|
||||||
|
rand_chacha = { version = "0.2", default-features = false, optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
sp-core = { version = "2.0.0-alpha.2", path = "../../primitives/core" }
|
sp-core = { version = "2.0.0-alpha.2", path = "../../primitives/core" }
|
||||||
pallet-balances = { version = "2.0.0-alpha.2", path = "../balances" }
|
pallet-balances = { version = "2.0.0-alpha.2", path = "../balances" }
|
||||||
pallet-timestamp = { version = "2.0.0-alpha.2", path = "../timestamp" }
|
pallet-timestamp = { version = "2.0.0-alpha.2", path = "../timestamp" }
|
||||||
pallet-staking-reward-curve = { version = "2.0.0-alpha.2", path = "../staking/reward-curve" }
|
pallet-staking-reward-curve = { version = "2.0.0-alpha.2", path = "../staking/reward-curve" }
|
||||||
substrate-test-utils = { version = "2.0.0-alpha.2", path = "../../test-utils" }
|
substrate-test-utils = { version = "2.0.0-alpha.2", path = "../../test-utils" }
|
||||||
|
frame-benchmarking = { version = "2.0.0-alpha.2", path = "../benchmarking" }
|
||||||
|
rand_chacha = { version = "0.2" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
migrate = []
|
migrate = []
|
||||||
@@ -46,3 +51,8 @@ std = [
|
|||||||
"frame-system/std",
|
"frame-system/std",
|
||||||
"pallet-authorship/std",
|
"pallet-authorship/std",
|
||||||
]
|
]
|
||||||
|
runtime-benchmarks = [
|
||||||
|
"rand_chacha",
|
||||||
|
"frame-benchmarking",
|
||||||
|
"frame-system/runtime-benchmarks",
|
||||||
|
]
|
||||||
|
|||||||
@@ -0,0 +1,464 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Staking pallet benchmarking.
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use rand_chacha::{rand_core::{RngCore, SeedableRng}, ChaChaRng};
|
||||||
|
|
||||||
|
use sp_runtime::traits::One;
|
||||||
|
use sp_io::hashing::blake2_256;
|
||||||
|
|
||||||
|
use frame_system::RawOrigin;
|
||||||
|
use frame_benchmarking::{benchmarks, account};
|
||||||
|
|
||||||
|
use crate::Module as Staking;
|
||||||
|
use frame_system::Module as System;
|
||||||
|
|
||||||
|
const SEED: u32 = 0;
|
||||||
|
|
||||||
|
fn create_funded_user<T: Trait>(string: &'static str, n: u32) -> T::AccountId {
|
||||||
|
let user = account(string, n, SEED);
|
||||||
|
let balance = T::Currency::minimum_balance() * 100.into();
|
||||||
|
T::Currency::make_free_balance_be(&user, balance);
|
||||||
|
user
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_stash_controller<T: Trait>(n: u32) -> Result<(T::AccountId, T::AccountId), &'static str> {
|
||||||
|
let stash = create_funded_user::<T>("stash", n);
|
||||||
|
let controller = create_funded_user::<T>("controller", n);
|
||||||
|
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();
|
||||||
|
Staking::<T>::bond(RawOrigin::Signed(stash.clone()).into(), controller_lookup, amount, reward_destination)?;
|
||||||
|
return Ok((stash, controller))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_validators<T: Trait>(max: u32) -> Result<Vec<<T::Lookup as StaticLookup>::Source>, &'static str> {
|
||||||
|
let mut validators: Vec<<T::Lookup as StaticLookup>::Source> = Vec::with_capacity(max as usize);
|
||||||
|
for i in 0 .. max {
|
||||||
|
let (stash, controller) = create_stash_controller::<T>(i)?;
|
||||||
|
let validator_prefs = ValidatorPrefs {
|
||||||
|
commission: Perbill::from_percent(50),
|
||||||
|
};
|
||||||
|
Staking::<T>::validate(RawOrigin::Signed(controller).into(), validator_prefs)?;
|
||||||
|
let stash_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(stash);
|
||||||
|
validators.push(stash_lookup);
|
||||||
|
}
|
||||||
|
Ok(validators)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
let mut rng = ChaChaRng::from_seed(SEED.using_encoded(blake2_256));
|
||||||
|
|
||||||
|
// Create v validators
|
||||||
|
for i in 0 .. v {
|
||||||
|
let (v_stash, v_controller) = create_stash_controller::<T>(i)?;
|
||||||
|
let validator_prefs = ValidatorPrefs {
|
||||||
|
commission: Perbill::from_percent(50),
|
||||||
|
};
|
||||||
|
Staking::<T>::validate(RawOrigin::Signed(v_controller.clone()).into(), validator_prefs)?;
|
||||||
|
let stash_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(v_stash.clone());
|
||||||
|
validators.push(stash_lookup.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create n nominators
|
||||||
|
for j in 0 .. n {
|
||||||
|
let (_n_stash, n_controller) = create_stash_controller::<T>(u32::max_value() - j)?;
|
||||||
|
|
||||||
|
// Have them randomly validate
|
||||||
|
let mut available_validators = validators.clone();
|
||||||
|
let mut selected_validators: Vec<<T::Lookup as StaticLookup>::Source> = Vec::with_capacity(MAX_NOMINATIONS);
|
||||||
|
for _ in 0 .. v.min(MAX_NOMINATIONS as u32) {
|
||||||
|
let selected = rng.next_u32() as usize % available_validators.len();
|
||||||
|
let validator = available_validators.remove(selected);
|
||||||
|
selected_validators.push(validator);
|
||||||
|
}
|
||||||
|
Staking::<T>::nominate(RawOrigin::Signed(n_controller.clone()).into(), selected_validators)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidatorCount::put(v);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function generates one validator being nominated by n nominators.
|
||||||
|
// It starts an era and creates pending payouts.
|
||||||
|
pub fn create_validator_with_nominators<T: Trait>(n: u32, upper_bound: u32) -> Result<T::AccountId, &'static str> {
|
||||||
|
let mut points_total = 0;
|
||||||
|
let mut points_individual = Vec::new();
|
||||||
|
|
||||||
|
MinimumValidatorCount::put(0);
|
||||||
|
|
||||||
|
let (v_stash, v_controller) = create_stash_controller::<T>(0)?;
|
||||||
|
let validator_prefs = ValidatorPrefs {
|
||||||
|
commission: Perbill::from_percent(50),
|
||||||
|
};
|
||||||
|
Staking::<T>::validate(RawOrigin::Signed(v_controller.clone()).into(), validator_prefs)?;
|
||||||
|
let stash_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(v_stash.clone());
|
||||||
|
|
||||||
|
points_total += 10;
|
||||||
|
points_individual.push((v_stash, 10));
|
||||||
|
|
||||||
|
// Give the validator n nominators, but keep total users in the system the same.
|
||||||
|
for i in 0 .. upper_bound {
|
||||||
|
let (_n_stash, n_controller) = create_stash_controller::<T>(u32::max_value() - i)?;
|
||||||
|
if i < n {
|
||||||
|
Staking::<T>::nominate(RawOrigin::Signed(n_controller.clone()).into(), vec![stash_lookup.clone()])?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidatorCount::put(1);
|
||||||
|
|
||||||
|
// Start a new Era
|
||||||
|
let new_validators = Staking::<T>::new_era(SessionIndex::one()).unwrap();
|
||||||
|
|
||||||
|
assert!(new_validators.len() == 1);
|
||||||
|
|
||||||
|
// Give Era Points
|
||||||
|
let reward = EraRewardPoints::<T::AccountId> {
|
||||||
|
total: points_total,
|
||||||
|
individual: points_individual.into_iter().collect(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let current_era = CurrentEra::get().unwrap();
|
||||||
|
ErasRewardPoints::<T>::insert(current_era, reward);
|
||||||
|
|
||||||
|
// Create reward pool
|
||||||
|
let total_payout = T::Currency::minimum_balance() * 1000.into();
|
||||||
|
<ErasValidatorReward<T>>::insert(current_era, total_payout);
|
||||||
|
|
||||||
|
Ok(v_controller)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function generates one nominator nominating v validators.
|
||||||
|
// It starts an era and creates pending payouts.
|
||||||
|
pub fn create_nominator_with_validators<T: Trait>(v: u32) -> Result<(T::AccountId, Vec<T::AccountId>), &'static str> {
|
||||||
|
let mut validators = Vec::new();
|
||||||
|
let mut points_total = 0;
|
||||||
|
let mut points_individual = Vec::new();
|
||||||
|
|
||||||
|
MinimumValidatorCount::put(0);
|
||||||
|
|
||||||
|
// Create v validators
|
||||||
|
let mut validator_lookups = Vec::new();
|
||||||
|
for i in 0 .. v {
|
||||||
|
let (v_stash, v_controller) = create_stash_controller::<T>(i)?;
|
||||||
|
let validator_prefs = ValidatorPrefs {
|
||||||
|
commission: Perbill::from_percent(50),
|
||||||
|
};
|
||||||
|
Staking::<T>::validate(RawOrigin::Signed(v_controller.clone()).into(), validator_prefs)?;
|
||||||
|
let stash_lookup: <T::Lookup as StaticLookup>::Source = T::Lookup::unlookup(v_stash.clone());
|
||||||
|
|
||||||
|
points_total += 10;
|
||||||
|
points_individual.push((v_stash.clone(), 10));
|
||||||
|
validator_lookups.push(stash_lookup);
|
||||||
|
// Add to the list if it is less than the number we want the nominator to have
|
||||||
|
if validators.len() < v as usize {
|
||||||
|
validators.push(v_stash.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a nominator
|
||||||
|
let (_n_stash, n_controller) = create_stash_controller::<T>(u32::max_value())?;
|
||||||
|
Staking::<T>::nominate(RawOrigin::Signed(n_controller.clone()).into(), validator_lookups)?;
|
||||||
|
|
||||||
|
ValidatorCount::put(v);
|
||||||
|
|
||||||
|
// Start a new Era
|
||||||
|
let new_validators = Staking::<T>::new_era(SessionIndex::one()).unwrap();
|
||||||
|
|
||||||
|
assert!(new_validators.len() == v as usize);
|
||||||
|
|
||||||
|
// Give Era Points
|
||||||
|
let reward = EraRewardPoints::<T::AccountId> {
|
||||||
|
total: points_total,
|
||||||
|
individual: points_individual.into_iter().collect(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let current_era = CurrentEra::get().unwrap();
|
||||||
|
ErasRewardPoints::<T>::insert(current_era, reward);
|
||||||
|
|
||||||
|
// Create reward pool
|
||||||
|
let total_payout = T::Currency::minimum_balance() * 1000.into();
|
||||||
|
<ErasValidatorReward<T>>::insert(current_era, total_payout);
|
||||||
|
|
||||||
|
Ok((n_controller, validators))
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmarks! {
|
||||||
|
_{
|
||||||
|
// User account seed
|
||||||
|
let u in 0 .. 1000 => ();
|
||||||
|
}
|
||||||
|
|
||||||
|
bond {
|
||||||
|
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 reward_destination = RewardDestination::Staked;
|
||||||
|
let amount = T::Currency::minimum_balance() * 10.into();
|
||||||
|
}: _(RawOrigin::Signed(stash), controller_lookup, amount, reward_destination)
|
||||||
|
|
||||||
|
bond_extra {
|
||||||
|
let u in ...;
|
||||||
|
let (stash, _) = create_stash_controller::<T>(u)?;
|
||||||
|
let max_additional = T::Currency::minimum_balance() * 10.into();
|
||||||
|
}: _(RawOrigin::Signed(stash), max_additional)
|
||||||
|
|
||||||
|
unbond {
|
||||||
|
let u in ...;
|
||||||
|
let (_, controller) = create_stash_controller::<T>(u)?;
|
||||||
|
let amount = T::Currency::minimum_balance() * 10.into();
|
||||||
|
}: _(RawOrigin::Signed(controller), amount)
|
||||||
|
|
||||||
|
// Worst case scenario, everything is removed after the bonding duration
|
||||||
|
withdraw_unbonded {
|
||||||
|
let u in ...;
|
||||||
|
let (stash, controller) = create_stash_controller::<T>(u)?;
|
||||||
|
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))
|
||||||
|
|
||||||
|
validate {
|
||||||
|
let u in ...;
|
||||||
|
let (_, controller) = create_stash_controller::<T>(u)?;
|
||||||
|
let prefs = ValidatorPrefs::default();
|
||||||
|
}: _(RawOrigin::Signed(controller), prefs)
|
||||||
|
|
||||||
|
// Worst case scenario, MAX_NOMINATIONS
|
||||||
|
nominate {
|
||||||
|
let n in 1 .. MAX_NOMINATIONS as u32;
|
||||||
|
let (_, controller) = create_stash_controller::<T>(n + 1)?;
|
||||||
|
let validators = create_validators::<T>(n)?;
|
||||||
|
}: _(RawOrigin::Signed(controller), validators)
|
||||||
|
|
||||||
|
chill {
|
||||||
|
let u in ...;
|
||||||
|
let (_, controller) = create_stash_controller::<T>(u)?;
|
||||||
|
}: _(RawOrigin::Signed(controller))
|
||||||
|
|
||||||
|
set_payee {
|
||||||
|
let u in ...;
|
||||||
|
let (_, controller) = create_stash_controller::<T>(u)?;
|
||||||
|
}: _(RawOrigin::Signed(controller), 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);
|
||||||
|
}: _(RawOrigin::Signed(stash), new_controller_lookup)
|
||||||
|
|
||||||
|
set_validator_count {
|
||||||
|
let c in 0 .. 1000;
|
||||||
|
}: _(RawOrigin::Root, c)
|
||||||
|
|
||||||
|
force_no_eras { let i in 1 .. 1; }: _(RawOrigin::Root)
|
||||||
|
|
||||||
|
force_new_era {let i in 1 .. 1; }: _(RawOrigin::Root)
|
||||||
|
|
||||||
|
force_new_era_always { let i in 1 .. 1; }: _(RawOrigin::Root)
|
||||||
|
|
||||||
|
// Worst case scenario, the list of invulnerables is very long.
|
||||||
|
set_invulnerables {
|
||||||
|
let v in 0 .. 1000;
|
||||||
|
let mut invulnerables = Vec::new();
|
||||||
|
for i in 0 .. v {
|
||||||
|
invulnerables.push(account("invulnerable", i, SEED));
|
||||||
|
}
|
||||||
|
}: _(RawOrigin::Root, invulnerables)
|
||||||
|
|
||||||
|
force_unstake {
|
||||||
|
let u in ...;
|
||||||
|
let (stash, _) = create_stash_controller::<T>(u)?;
|
||||||
|
}: _(RawOrigin::Root, stash)
|
||||||
|
|
||||||
|
cancel_deferred_slash {
|
||||||
|
let s in 1 .. 1000;
|
||||||
|
let mut unapplied_slashes = Vec::new();
|
||||||
|
let era = EraIndex::one();
|
||||||
|
for _ in 0 .. 1000 {
|
||||||
|
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)
|
||||||
|
|
||||||
|
payout_validator {
|
||||||
|
let n in 1 .. MAX_NOMINATIONS as u32;
|
||||||
|
let validator = create_validator_with_nominators::<T>(n, MAX_NOMINATIONS as u32)?;
|
||||||
|
let current_era = CurrentEra::get().unwrap();
|
||||||
|
}: _(RawOrigin::Signed(validator), current_era)
|
||||||
|
|
||||||
|
payout_nominator {
|
||||||
|
let v in 0 .. MAX_NOMINATIONS as u32;
|
||||||
|
let (nominator, validators) = create_nominator_with_validators::<T>(v)?;
|
||||||
|
let current_era = CurrentEra::get().unwrap();
|
||||||
|
let find_nominator = validators.into_iter().map(|x| (x, 0)).collect();
|
||||||
|
}: _(RawOrigin::Signed(nominator), current_era, find_nominator)
|
||||||
|
|
||||||
|
rebond {
|
||||||
|
let l in 1 .. 1000;
|
||||||
|
let (_, controller) = create_stash_controller::<T>(u)?;
|
||||||
|
let mut staking_ledger = Ledger::<T>::get(controller.clone()).unwrap();
|
||||||
|
let unlock_chunk = UnlockChunk::<BalanceOf<T>> {
|
||||||
|
value: 1.into(),
|
||||||
|
era: EraIndex::zero(),
|
||||||
|
};
|
||||||
|
for _ in 0 .. l {
|
||||||
|
staking_ledger.unlocking.push(unlock_chunk.clone())
|
||||||
|
}
|
||||||
|
Ledger::<T>::insert(controller.clone(), staking_ledger);
|
||||||
|
}: _(RawOrigin::Signed(controller), (l + 100).into())
|
||||||
|
|
||||||
|
set_history_depth {
|
||||||
|
let e in 1 .. 100;
|
||||||
|
HistoryDepth::put(e);
|
||||||
|
CurrentEra::put(e);
|
||||||
|
for i in 0 .. e {
|
||||||
|
<ErasStakers<T>>::insert(i, T::AccountId::default(), Exposure::<T::AccountId, BalanceOf<T>>::default());
|
||||||
|
<ErasStakersClipped<T>>::insert(i, T::AccountId::default(), Exposure::<T::AccountId, BalanceOf<T>>::default());
|
||||||
|
<ErasValidatorPrefs<T>>::insert(i, T::AccountId::default(), ValidatorPrefs::default());
|
||||||
|
<ErasValidatorReward<T>>::insert(i, BalanceOf::<T>::one());
|
||||||
|
<ErasRewardPoints<T>>::insert(i, EraRewardPoints::<T::AccountId>::default());
|
||||||
|
<ErasTotalStake<T>>::insert(i, BalanceOf::<T>::one());
|
||||||
|
ErasStartSessionIndex::insert(i, i);
|
||||||
|
}
|
||||||
|
}: _(RawOrigin::Root, EraIndex::zero())
|
||||||
|
|
||||||
|
reap_stash {
|
||||||
|
let u in 1 .. 1000;
|
||||||
|
let (stash, controller) = create_stash_controller::<T>(u)?;
|
||||||
|
T::Currency::make_free_balance_be(&stash, 0.into());
|
||||||
|
}: _(RawOrigin::Signed(controller), stash)
|
||||||
|
|
||||||
|
new_era {
|
||||||
|
let v in 1 .. 10;
|
||||||
|
let n in 1 .. 100;
|
||||||
|
MinimumValidatorCount::put(0);
|
||||||
|
create_validators_with_nominators_for_era::<T>(v, n)?;
|
||||||
|
let session_index = SessionIndex::one();
|
||||||
|
}: {
|
||||||
|
let validators = Staking::<T>::new_era(session_index).ok_or("`new_era` failed")?;
|
||||||
|
assert!(validators.len() == v as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_slash {
|
||||||
|
let l in 1 .. 1000;
|
||||||
|
let (stash, controller) = create_stash_controller::<T>(0)?;
|
||||||
|
let mut staking_ledger = Ledger::<T>::get(controller.clone()).unwrap();
|
||||||
|
let unlock_chunk = UnlockChunk::<BalanceOf<T>> {
|
||||||
|
value: 1.into(),
|
||||||
|
era: EraIndex::zero(),
|
||||||
|
};
|
||||||
|
for _ in 0 .. l {
|
||||||
|
staking_ledger.unlocking.push(unlock_chunk.clone())
|
||||||
|
}
|
||||||
|
Ledger::<T>::insert(controller.clone(), staking_ledger.clone());
|
||||||
|
let slash_amount = T::Currency::minimum_balance() * 10.into();
|
||||||
|
}: {
|
||||||
|
crate::slashing::do_slash::<T>(
|
||||||
|
&stash,
|
||||||
|
slash_amount,
|
||||||
|
&mut BalanceOf::<T>::zero(),
|
||||||
|
&mut NegativeImbalanceOf::<T>::zero()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::*;
|
||||||
|
use crate::mock::*;
|
||||||
|
use frame_support::assert_ok;
|
||||||
|
|
||||||
|
use crate::benchmarking::{
|
||||||
|
create_validators_with_nominators_for_era,
|
||||||
|
create_validator_with_nominators,
|
||||||
|
create_nominator_with_validators,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_validators_with_nominators_for_era_works() {
|
||||||
|
ExtBuilder::default().stakers(false).build().execute_with(|| {
|
||||||
|
let v = 10;
|
||||||
|
let n = 100;
|
||||||
|
|
||||||
|
create_validators_with_nominators_for_era::<Test>(v,n).unwrap();
|
||||||
|
|
||||||
|
let count_validators = Validators::<Test>::iter().count();
|
||||||
|
let count_nominators = Nominators::<Test>::iter().count();
|
||||||
|
|
||||||
|
assert_eq!(count_validators, v as usize);
|
||||||
|
assert_eq!(count_nominators, n as usize);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_validator_with_nominators_works() {
|
||||||
|
ExtBuilder::default().stakers(false).build().execute_with(|| {
|
||||||
|
let n = 10;
|
||||||
|
|
||||||
|
let validator = create_validator_with_nominators::<Test>(
|
||||||
|
n,
|
||||||
|
MAX_NOMINATIONS as u32,
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
let current_era = CurrentEra::get().unwrap();
|
||||||
|
let controller = validator;
|
||||||
|
let ledger = Staking::ledger(&controller).unwrap();
|
||||||
|
let stash = &ledger.stash;
|
||||||
|
|
||||||
|
let original_free_balance = Balances::free_balance(stash);
|
||||||
|
assert_ok!(Staking::payout_validator(Origin::signed(controller), current_era));
|
||||||
|
let new_free_balance = Balances::free_balance(stash);
|
||||||
|
|
||||||
|
assert!(original_free_balance < new_free_balance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_nominator_with_validators_works() {
|
||||||
|
ExtBuilder::default().stakers(false).build().execute_with(|| {
|
||||||
|
let v = 5;
|
||||||
|
|
||||||
|
let (nominator, validators) = create_nominator_with_validators::<Test>(v).unwrap();
|
||||||
|
|
||||||
|
let current_era = CurrentEra::get().unwrap();
|
||||||
|
let controller = nominator;
|
||||||
|
let ledger = Staking::ledger(&controller).unwrap();
|
||||||
|
let stash = &ledger.stash;
|
||||||
|
|
||||||
|
let find_nominator = validators.into_iter().map(|x| (x, 0)).collect();
|
||||||
|
|
||||||
|
let original_free_balance = Balances::free_balance(stash);
|
||||||
|
assert_ok!(Staking::payout_nominator(Origin::signed(controller), current_era, find_nominator));
|
||||||
|
let new_free_balance = Balances::free_balance(stash);
|
||||||
|
|
||||||
|
assert!(original_free_balance < new_free_balance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -254,6 +254,8 @@ mod mock;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
mod slashing;
|
mod slashing;
|
||||||
|
#[cfg(any(feature = "runtime-benchmarks", test))]
|
||||||
|
pub mod benchmarking;
|
||||||
|
|
||||||
pub mod inflation;
|
pub mod inflation;
|
||||||
|
|
||||||
@@ -287,7 +289,7 @@ use sp_phragmen::ExtendedBalance;
|
|||||||
use frame_support::traits::MigrateAccount;
|
use frame_support::traits::MigrateAccount;
|
||||||
|
|
||||||
const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4;
|
const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4;
|
||||||
const MAX_NOMINATIONS: usize = 16;
|
pub const MAX_NOMINATIONS: usize = 16;
|
||||||
const MAX_UNLOCKING_CHUNKS: usize = 32;
|
const MAX_UNLOCKING_CHUNKS: usize = 32;
|
||||||
const STAKING_ID: LockIdentifier = *b"staking ";
|
const STAKING_ID: LockIdentifier = *b"staking ";
|
||||||
|
|
||||||
@@ -1281,6 +1283,8 @@ decl_module! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----- Root calls.
|
||||||
|
|
||||||
/// The ideal number of validators.
|
/// The ideal number of validators.
|
||||||
#[weight = SimpleDispatchInfo::FixedNormal(5_000)]
|
#[weight = SimpleDispatchInfo::FixedNormal(5_000)]
|
||||||
fn set_validator_count(origin, #[compact] new: u32) {
|
fn set_validator_count(origin, #[compact] new: u32) {
|
||||||
@@ -1288,8 +1292,6 @@ decl_module! {
|
|||||||
ValidatorCount::put(new);
|
ValidatorCount::put(new);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- Root calls.
|
|
||||||
|
|
||||||
/// Force there to be no new eras indefinitely.
|
/// Force there to be no new eras indefinitely.
|
||||||
///
|
///
|
||||||
/// # <weight>
|
/// # <weight>
|
||||||
@@ -1357,7 +1359,7 @@ decl_module! {
|
|||||||
.or_else(ensure_root)?;
|
.or_else(ensure_root)?;
|
||||||
|
|
||||||
ensure!(!slash_indices.is_empty(), Error::<T>::EmptyTargets);
|
ensure!(!slash_indices.is_empty(), Error::<T>::EmptyTargets);
|
||||||
ensure!(Self::is_sorted_and_unique(&slash_indices), Error::<T>::NotSortedAndUnique);
|
ensure!(is_sorted_and_unique(&slash_indices), Error::<T>::NotSortedAndUnique);
|
||||||
|
|
||||||
let mut unapplied = <Self as Store>::UnappliedSlashes::get(&era);
|
let mut unapplied = <Self as Store>::UnappliedSlashes::get(&era);
|
||||||
let last_item = slash_indices[slash_indices.len() - 1];
|
let last_item = slash_indices[slash_indices.len() - 1];
|
||||||
@@ -1510,11 +1512,6 @@ impl<T: Trait> Module<T> {
|
|||||||
Self::bonded(stash).and_then(Self::ledger).map(|l| l.active).unwrap_or_default()
|
Self::bonded(stash).and_then(Self::ledger).map(|l| l.active).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check that list is sorted and has no duplicates.
|
|
||||||
fn is_sorted_and_unique(list: &Vec<u32>) -> bool {
|
|
||||||
list.windows(2).all(|w| w[0] < w[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
// MUTABLES (DANGEROUS)
|
// MUTABLES (DANGEROUS)
|
||||||
|
|
||||||
fn do_payout_nominator(who: T::AccountId, era: EraIndex, validators: Vec<(T::AccountId, u32)>)
|
fn do_payout_nominator(who: T::AccountId, era: EraIndex, validators: Vec<(T::AccountId, u32)>)
|
||||||
@@ -2220,3 +2217,8 @@ impl<T, Reporter, Offender, R, O> ReportOffence<Reporter, Offender, O>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check that list is sorted and has no duplicates.
|
||||||
|
fn is_sorted_and_unique(list: &[u32]) -> bool {
|
||||||
|
list.windows(2).all(|w| w[0] < w[1])
|
||||||
|
}
|
||||||
|
|||||||
@@ -226,6 +226,7 @@ pub struct ExtBuilder {
|
|||||||
fair: bool,
|
fair: bool,
|
||||||
num_validators: Option<u32>,
|
num_validators: Option<u32>,
|
||||||
invulnerables: Vec<u64>,
|
invulnerables: Vec<u64>,
|
||||||
|
stakers: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ExtBuilder {
|
impl Default for ExtBuilder {
|
||||||
@@ -240,6 +241,7 @@ impl Default for ExtBuilder {
|
|||||||
fair: true,
|
fair: true,
|
||||||
num_validators: None,
|
num_validators: None,
|
||||||
invulnerables: vec![],
|
invulnerables: vec![],
|
||||||
|
stakers: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,6 +287,11 @@ impl ExtBuilder {
|
|||||||
EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit);
|
EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit);
|
||||||
SLASH_DEFER_DURATION.with(|v| *v.borrow_mut() = self.slash_defer_duration);
|
SLASH_DEFER_DURATION.with(|v| *v.borrow_mut() = self.slash_defer_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn stakers(mut self, has_stakers: bool) -> Self {
|
||||||
|
self.stakers = has_stakers;
|
||||||
|
self
|
||||||
|
}
|
||||||
pub fn build(self) -> sp_io::TestExternalities {
|
pub fn build(self) -> sp_io::TestExternalities {
|
||||||
self.set_associated_consts();
|
self.set_associated_consts();
|
||||||
let mut storage = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
let mut storage = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||||
@@ -320,16 +327,17 @@ impl ExtBuilder {
|
|||||||
],
|
],
|
||||||
}.assimilate_storage(&mut storage);
|
}.assimilate_storage(&mut storage);
|
||||||
|
|
||||||
let stake_21 = if self.fair { 1000 } else { 2000 };
|
let mut stakers = vec![];
|
||||||
let stake_31 = if self.validator_pool { balance_factor * 1000 } else { 1 };
|
if self.stakers {
|
||||||
let status_41 = if self.validator_pool {
|
let stake_21 = if self.fair { 1000 } else { 2000 };
|
||||||
StakerStatus::<AccountId>::Validator
|
let stake_31 = if self.validator_pool { balance_factor * 1000 } else { 1 };
|
||||||
} else {
|
let status_41 = if self.validator_pool {
|
||||||
StakerStatus::<AccountId>::Idle
|
StakerStatus::<AccountId>::Validator
|
||||||
};
|
} else {
|
||||||
let nominated = if self.nominate { vec![11, 21] } else { vec![] };
|
StakerStatus::<AccountId>::Idle
|
||||||
let _ = GenesisConfig::<Test>{
|
};
|
||||||
stakers: vec![
|
let nominated = if self.nominate { vec![11, 21] } else { vec![] };
|
||||||
|
stakers = vec![
|
||||||
// (stash, controller, staked_amount, status)
|
// (stash, controller, staked_amount, status)
|
||||||
(11, 10, balance_factor * 1000, StakerStatus::<AccountId>::Validator),
|
(11, 10, balance_factor * 1000, StakerStatus::<AccountId>::Validator),
|
||||||
(21, 20, stake_21, StakerStatus::<AccountId>::Validator),
|
(21, 20, stake_21, StakerStatus::<AccountId>::Validator),
|
||||||
@@ -337,7 +345,10 @@ impl ExtBuilder {
|
|||||||
(41, 40, balance_factor * 1000, status_41),
|
(41, 40, balance_factor * 1000, status_41),
|
||||||
// nominator
|
// nominator
|
||||||
(101, 100, balance_factor * 500, StakerStatus::<AccountId>::Nominator(nominated))
|
(101, 100, balance_factor * 500, StakerStatus::<AccountId>::Nominator(nominated))
|
||||||
],
|
];
|
||||||
|
}
|
||||||
|
let _ = GenesisConfig::<Test>{
|
||||||
|
stakers: stakers,
|
||||||
validator_count: self.validator_count,
|
validator_count: self.validator_count,
|
||||||
minimum_validator_count: self.minimum_validator_count,
|
minimum_validator_count: self.minimum_validator_count,
|
||||||
invulnerables: self.invulnerables,
|
invulnerables: self.invulnerables,
|
||||||
|
|||||||
@@ -566,7 +566,7 @@ pub(crate) fn clear_stash_metadata<T: Trait>(stash: &T::AccountId) {
|
|||||||
// apply the slash to a stash account, deducting any missing funds from the reward
|
// apply the slash to a stash account, deducting any missing funds from the reward
|
||||||
// payout, saturating at 0. this is mildly unfair but also an edge-case that
|
// payout, saturating at 0. this is mildly unfair but also an edge-case that
|
||||||
// can only occur when overlapping locked funds have been slashed.
|
// can only occur when overlapping locked funds have been slashed.
|
||||||
fn do_slash<T: Trait>(
|
pub fn do_slash<T: Trait>(
|
||||||
stash: &T::AccountId,
|
stash: &T::AccountId,
|
||||||
value: BalanceOf<T>,
|
value: BalanceOf<T>,
|
||||||
reward_payout: &mut BalanceOf<T>,
|
reward_payout: &mut BalanceOf<T>,
|
||||||
|
|||||||
Reference in New Issue
Block a user