diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index eccf9751322ca..5218719074151 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -629,6 +629,12 @@ pub trait IsMember { fn is_member(member_id: &MemberId) -> bool; } +/// Determine if an `AuthorityId` is online in the current session. +pub trait IsOnline { + /// Is the given `AuthorityId` online in the current session? + fn is_online_in_current_session(authority_id: &AuthorityId) -> bool; +} + /// Something which fulfills the abstract idea of a Substrate header. It has types for a `Number`, /// a `Hash` and a `Digest`. It provides access to an `extrinsics_root`, `state_root` and /// `parent_hash`, as well as a `digest` and a block `number`. diff --git a/node-template/runtime/src/lib.rs b/node-template/runtime/src/lib.rs index 332df0ec0dfae..a5a706adfa3d0 100644 --- a/node-template/runtime/src/lib.rs +++ b/node-template/runtime/src/lib.rs @@ -17,7 +17,10 @@ use primitives::bytes; use primitives::{ed25519, sr25519, OpaqueMetadata}; use sr_primitives::{ ApplyResult, transaction_validity::TransactionValidity, generic, create_runtime_str, - traits::{self, NumberFor, BlakeTwo256, Block as BlockT, StaticLookup, Verify}, weights::Weight, + traits::{ + self, NumberFor, BlakeTwo256, Block as BlockT, IsOnline, StaticLookup, Verify, + }, + weights::Weight, }; use client::{ block_builder::api::{CheckInherentsResult, InherentData, self as block_builder_api}, @@ -138,6 +141,13 @@ impl system::Trait for Runtime { impl aura::Trait for Runtime { type HandleReport = (); type AuthorityId = AuraId; + type IsOnline = Runtime; +} + +impl IsOnline for Runtime { + fn is_online_in_current_session(authority_id: &AuraId) -> bool { + true + } } impl indices::Trait for Runtime { diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index aa7ba04af3335..22cae88cfbde9 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -38,7 +38,7 @@ use runtime_primitives::{ApplyResult, impl_opaque_keys, generic, create_runtime_ use runtime_primitives::transaction_validity::TransactionValidity; use runtime_primitives::weights::Weight; use runtime_primitives::traits::{ - BlakeTwo256, Block as BlockT, DigestFor, NumberFor, StaticLookup, + BlakeTwo256, Block as BlockT, DigestFor, IsOnline, NumberFor, StaticLookup, }; use version::RuntimeVersion; use elections::VoteIndex; @@ -78,8 +78,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to equal spec_version. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 118, - impl_version: 118, + spec_version: 119, + impl_version: 119, apis: RUNTIME_API_VERSIONS, }; @@ -143,6 +143,7 @@ impl system::Trait for Runtime { impl aura::Trait for Runtime { type HandleReport = aura::StakingSlasher; type AuthorityId = AuraId; + type IsOnline = Runtime; } impl indices::Trait for Runtime { @@ -388,6 +389,12 @@ impl im_online::Trait for Runtime { type IsValidAuthorityId = Aura; } +impl IsOnline for Runtime { + fn is_online_in_current_session(authority_id: &AuraId) -> bool { + ImOnline::is_online_in_current_session(authority_id) + } +} + impl grandpa::Trait for Runtime { type Event = Event; } diff --git a/srml/aura/src/lib.rs b/srml/aura/src/lib.rs index a0f7ad6242429..01a5cf1e68a83 100644 --- a/srml/aura/src/lib.rs +++ b/srml/aura/src/lib.rs @@ -54,7 +54,7 @@ use rstd::{result, prelude::*}; use parity_codec::Encode; use srml_support::{decl_storage, decl_module, Parameter, storage::StorageValue, traits::Get}; use primitives::{ - traits::{SaturatedConversion, Saturating, Zero, One, Member, IsMember, TypedKey}, + traits::{SaturatedConversion, Saturating, Zero, One, Member, IsMember, IsOnline, TypedKey}, generic::DigestItem, }; use timestamp::OnTimestampSet; @@ -157,6 +157,9 @@ pub trait Trait: timestamp::Trait { /// The identifier type for an authority. type AuthorityId: Member + Parameter + TypedKey + Default; + + /// Determine if an `AuthorityId` is online. + type IsOnline: IsOnline; } decl_storage! { @@ -297,8 +300,19 @@ impl HandleReport for StakingSlasher { report.punish( validators.len(), |idx, slash_count| { - let v = validators[idx].clone(); - staking::Module::::on_offline_validator(v, slash_count); + // slash only if the validator isn't marked as online in the current session + // TODO maybe define AuthorityOf(AccountId) instead of putting this here + let account_id = validators[idx].clone(); + let keys = T::SessionInterface::current_keys::(); + let maybe_aura_id = keys + .iter() + .find(|(k, _)| *k == account_id) + .map(|(_, a)| a); + if let Some(aura_id) = maybe_aura_id { + if !T::IsOnline::is_online_in_current_session(aura_id) { + staking::Module::::on_offline_validator(account_id, slash_count); + } + } } ); } diff --git a/srml/aura/src/mock.rs b/srml/aura/src/mock.rs index 4348f6af2da4e..e82d713bd6ee5 100644 --- a/srml/aura/src/mock.rs +++ b/srml/aura/src/mock.rs @@ -19,7 +19,7 @@ #![cfg(test)] use primitives::{ - traits::IdentityLookup, + traits::{IdentityLookup, IsOnline}, testing::{Header, UintAuthorityId}, }; use srml_support::{impl_outer_origin, parameter_types}; @@ -67,6 +67,13 @@ impl timestamp::Trait for Test { impl Trait for Test { type HandleReport = (); type AuthorityId = UintAuthorityId; + type IsOnline = Test; +} + +impl IsOnline for Test { + fn is_online_in_current_session(authority_id: &UintAuthorityId) -> bool { + true + } } pub fn new_test_ext(authorities: Vec) -> runtime_io::TestExternalities { diff --git a/srml/session/src/lib.rs b/srml/session/src/lib.rs index 5495cdbe6535e..eaabc066556ad 100644 --- a/srml/session/src/lib.rs +++ b/srml/session/src/lib.rs @@ -304,6 +304,9 @@ decl_storage! { /// will be used to determine the validator's session keys. QueuedKeys get(queued_keys): Vec<(T::ValidatorId, T::Keys)>; + /// Returns the keys of the current session for all validators. + CurrentKeys get(current_keys): Vec<(T::ValidatorId, T::Keys)>; + } add_extra_genesis { config(keys): Vec<(T::ValidatorId, T::Keys)>; @@ -439,6 +442,19 @@ impl Module { // Tell everyone about the new session keys. T::SessionHandler::on_new_session::(changed, &session_keys, &queued_amalgamated); + + >::put(session_keys); + } + + pub fn get_current_keys() -> Vec<(T::ValidatorId, Key)> { + >::get() + .into_iter() + .map(|(v, k)| { + let tk= k.get::(::KEY_TYPE) + .unwrap_or_default(); + (v, tk) + }) + .collect() } /// Disable the validator of index `i`. diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index 6894cf5c97c36..caf0a3e1b0e6a 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -285,6 +285,7 @@ use session::{historical::OnSessionEnding, SelectInitialValidators, SessionIndex use primitives::Perbill; use primitives::traits::{ Convert, Zero, One, StaticLookup, CheckedSub, CheckedShl, Saturating, Bounded, + TypedKey, }; #[cfg(feature = "std")] use primitives::{Serialize, Deserialize}; @@ -455,6 +456,8 @@ pub trait SessionInterface: system::Trait { fn validators() -> Vec; /// Prune historical session tries up to but not including the given index. fn prune_historical_up_to(up_to: session::SessionIndex); + /// Get the keys of the current session. + fn current_keys() -> Vec<(AccountId, Key)>; } impl SessionInterface<::AccountId> for T where @@ -476,6 +479,12 @@ impl SessionInterface<::AccountId> for T where >::validators() } + fn current_keys() -> Vec<(::AccountId, Key)> + where Key: Decode + Default + TypedKey + { + >::get_current_keys::() + } + fn prune_historical_up_to(up_to: session::SessionIndex) { >::prune_up_to(up_to); }