diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index d243357fc2e30..c4a21a42fdbda 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -138,6 +138,8 @@ impl timestamp::Trait for Runtime { impl session::Trait for Runtime { type ConvertAccountIdToSessionKey = (); type OnSessionChange = (Staking, grandpa::SyncedAuthorities); + type OnDisable = Staking; + type CheckRotateSession = session::AuraCheckRotateSession; type Event = Event; } diff --git a/srml/session/src/lib.rs b/srml/session/src/lib.rs index d13795e4bb09c..0bb342c6465e5 100644 --- a/srml/session/src/lib.rs +++ b/srml/session/src/lib.rs @@ -118,7 +118,7 @@ use rstd::prelude::*; use primitives::traits::{Zero, One, Convert}; use srml_support::{StorageValue, StorageMap, for_each_tuple, decl_module, decl_event, decl_storage}; -use srml_support::{dispatch::Result, traits::OnFreeBalanceZero}; +use srml_support::traits::OnFreeBalanceZero; use system::ensure_signed; use rstd::ops::Mul; @@ -128,6 +128,12 @@ pub trait OnSessionChange { fn on_session_change(time_elapsed: T, should_reward: bool); } +/// A validator was disabled. +pub trait OnDisable { + /// Validator was disabled. + fn on_disable(account_id: T); +} + macro_rules! impl_session_change { () => ( impl OnSessionChange for () { @@ -146,6 +152,45 @@ macro_rules! impl_session_change { for_each_tuple!(impl_session_change); +macro_rules! impl_disable { + () => ( + impl OnDisable for () { + fn on_disable(_: T) {} + } + ); + + ( $($t:ident)* ) => { + impl),*> OnDisable for ($($t,)*) { + fn on_disable(account_id: T) { + $($t::on_disable(account_id.clone());)* + } + } + } +} + +for_each_tuple!(impl_disable); + +pub trait CheckRotateSession { + fn check_rotate_session(block_number: BlockNumber); +} + +pub struct AuraCheckRotateSession(rstd::marker::PhantomData); + +impl CheckRotateSession for AuraCheckRotateSession { + /// Hook to be called after transaction processing. + fn check_rotate_session(block_number: T::BlockNumber) { + // Do this last, after the staking system has had the chance to switch out the authorities for the + // new set. + // Check block number and call `rotate_session` if necessary. + let is_final_block = ((block_number - >::last_length_change()) % >::length()).is_zero(); + let (should_end_session, apply_rewards) = >::take() + .map_or((is_final_block, is_final_block), |apply_rewards| (true, apply_rewards)); + if should_end_session { + >::rotate_session(is_final_block, apply_rewards); + } + } +} + pub trait Trait: timestamp::Trait + consensus::Trait { /// Create a session key from an account key. type ConvertAccountIdToSessionKey: Convert>; @@ -153,6 +198,12 @@ pub trait Trait: timestamp::Trait + consensus::Trait { /// Handler when a session changes. type OnSessionChange: OnSessionChange; + /// Handler when a validator is disabled. + type OnDisable: OnDisable; + + /// Hook for rotating sessions. + type CheckRotateSession: CheckRotateSession; + /// The overarching event type. type Event: From> + Into<::Event>; } @@ -175,8 +226,8 @@ decl_module! { } /// Forces a new session. - fn force_new_session(apply_rewards: bool) -> Result { - Self::apply_force_new_session(apply_rewards) + fn force_new_session(apply_rewards: bool) { + Self::apply_force_new_session(apply_rewards); } /// Called when a block is finalized. Will rotate session if it is the last @@ -237,9 +288,8 @@ impl Module { // INTERNAL API (available to other runtime modules) /// Forces a new session, no origin. - pub fn apply_force_new_session(apply_rewards: bool) -> Result { + pub fn apply_force_new_session(apply_rewards: bool) { >::put(apply_rewards); - Ok(()) } /// Set the current set of validators. @@ -252,15 +302,7 @@ impl Module { /// Hook to be called after transaction processing. pub fn check_rotate_session(block_number: T::BlockNumber) { - // Do this last, after the staking system has had the chance to switch out the authorities for the - // new set. - // Check block number and call `rotate_session` if necessary. - let is_final_block = ((block_number - Self::last_length_change()) % Self::length()).is_zero(); - let (should_end_session, apply_rewards) = >::take() - .map_or((is_final_block, is_final_block), |apply_rewards| (true, apply_rewards)); - if should_end_session { - Self::rotate_session(is_final_block, apply_rewards); - } + T::CheckRotateSession::check_rotate_session(block_number); } /// Move on to next session: register the new authority set. @@ -318,6 +360,11 @@ impl Module { let block_number = >::block_number(); length_minus_1 - (block_number - Self::last_length_change() + length_minus_1) % length } + + /// Disables a validator. + pub fn disable_validator(val: T::AccountId) { + T::OnDisable::on_disable(val); + } } impl OnFreeBalanceZero for Module { @@ -343,12 +390,21 @@ mod tests { thread_local!{ static NEXT_VALIDATORS: RefCell> = RefCell::new(vec![1, 2, 3]); + static DISABLED_VALIDATORS: RefCell> = RefCell::new(vec![]); } pub struct TestOnSessionChange; impl OnSessionChange for TestOnSessionChange { fn on_session_change(_elapsed: u64, _should_reward: bool) { NEXT_VALIDATORS.with(|v| Session::set_validators(&*v.borrow())); + DISABLED_VALIDATORS.with(|v| v.borrow_mut().clear()); + } + } + + pub struct TestOnDisable; + impl OnDisable for TestOnDisable { + fn on_disable(account_id: u64) { + DISABLED_VALIDATORS.with(|v| v.borrow_mut().push(account_id)); } } @@ -379,6 +435,8 @@ mod tests { impl Trait for Test { type ConvertAccountIdToSessionKey = ConvertUintAuthorityId; type OnSessionChange = TestOnSessionChange; + type OnDisable = TestOnDisable; + type CheckRotateSession = AuraCheckRotateSession; type Event = (); } diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index f84bf0a9ceac4..2f202b813ef0e 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -245,7 +245,7 @@ use runtime_io::with_storage; use rstd::{prelude::*, result, collections::btree_map::BTreeMap}; use parity_codec::{HasCompact, Encode, Decode}; -use srml_support::{StorageValue, StorageMap, EnumerableStorageMap, dispatch::Result}; +use srml_support::{StorageValue, StorageMap, EnumerableStorageMap}; use srml_support::{decl_module, decl_event, decl_storage, ensure}; use srml_support::traits::{ Currency, OnFreeBalanceZero, OnDilution, LockIdentifier, LockableCurrency, WithdrawReasons, @@ -750,8 +750,8 @@ decl_module! { /// Force there to be a new era. This also forces a new session immediately after. /// `apply_rewards` should be true for validators to get the session reward. - fn force_new_era(apply_rewards: bool) -> Result { - Self::apply_force_new_era(apply_rewards) + fn force_new_era(apply_rewards: bool) { + Self::apply_force_new_era(apply_rewards); } /// Set the offline slash grace period. @@ -780,9 +780,9 @@ decl_event!( impl Module { /// Just force_new_era without origin check. - fn apply_force_new_era(apply_rewards: bool) -> Result { + fn apply_force_new_era(apply_rewards: bool) { >::put(()); - >::apply_force_new_session(apply_rewards) + >::apply_force_new_session(apply_rewards); } // PUBLIC IMMUTABLES @@ -1104,7 +1104,7 @@ impl Module { .unwrap_or(slash_exposure); let _ = Self::slash_validator(&stash, slash); >::remove(&stash); - let _ = Self::apply_force_new_era(false); + >::disable_validator(controller); RawEvent::OfflineSlash(stash.clone(), slash) } else { @@ -1122,6 +1122,12 @@ impl OnSessionChange for Module { } } +impl session::OnDisable for Module { + fn on_disable(_account: T::AccountId) { + Self::apply_force_new_era(false); + } +} + impl OnFreeBalanceZero for Module { fn on_free_balance_zero(stash: &T::AccountId) { if let Some(controller) = >::take(stash) { diff --git a/srml/staking/src/mock.rs b/srml/staking/src/mock.rs index 88b401b19eb51..c3fe31eb781e7 100644 --- a/srml/staking/src/mock.rs +++ b/srml/staking/src/mock.rs @@ -76,6 +76,8 @@ impl balances::Trait for Test { impl session::Trait for Test { type ConvertAccountIdToSessionKey = ConvertUintAuthorityId; type OnSessionChange = Staking; + type OnDisable = Staking; + type CheckRotateSession = session::AuraCheckRotateSession; type Event = (); } impl timestamp::Trait for Test {