diff --git a/Cargo.lock b/Cargo.lock index 65e3d2dfdee35..32b71bc9f58a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -454,7 +454,6 @@ dependencies = [ "sr-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", "sr-std 0.1.0 (git+https://github.com/chainpool/substrate)", "sr-version 0.1.0 (git+https://github.com/chainpool/substrate)", - "srml-aura 0.1.0 (git+https://github.com/chainpool/substrate)", "srml-balances 0.1.0 (git+https://github.com/chainpool/substrate)", "srml-consensus 0.1.0 (git+https://github.com/chainpool/substrate)", "srml-indices 0.1.0 (git+https://github.com/chainpool/substrate)", @@ -467,6 +466,7 @@ dependencies = [ "substrate-finality-grandpa-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", "substrate-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", "xr-primitives 0.4.0", + "xrml-aura 0.1.0", "xrml-bridge-bitcoin 0.4.0", "xrml-fee-manager 0.4.0", "xrml-grandpa 0.1.0", @@ -4510,6 +4510,28 @@ dependencies = [ "substrate-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", ] +[[package]] +name = "xrml-aura" +version = "0.1.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec-derive 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 0.1.0 (git+https://github.com/chainpool/substrate)", + "sr-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", + "sr-std 0.1.0 (git+https://github.com/chainpool/substrate)", + "srml-consensus 0.1.0 (git+https://github.com/chainpool/substrate)", + "srml-support 0.1.0 (git+https://github.com/chainpool/substrate)", + "srml-system 0.1.0 (git+https://github.com/chainpool/substrate)", + "srml-timestamp 0.1.0 (git+https://github.com/chainpool/substrate)", + "substrate-inherents 0.1.0 (git+https://github.com/chainpool/substrate)", + "substrate-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", + "xrml-mining-staking 0.4.0", +] + [[package]] name = "xrml-bridge-bitcoin" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index bbca4707334e1..39907c372a5e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ members = [ "xrml/xaccounts", "xrml/xsession", "xrml/xgrandpa", + "xrml/xaura", # xrml/xfee "xrml/xfee/manager", # xrml/assets diff --git a/cli/src/genesis_config.rs b/cli/src/genesis_config.rs index b16077639c74f..b2f33fddf9506 100644 --- a/cli/src/genesis_config.rs +++ b/cli/src/genesis_config.rs @@ -17,7 +17,7 @@ use chainx_runtime::{ Runtime, }; use chainx_runtime::{ - BalancesConfig, ConsensusConfig, GenesisConfig, IndicesConfig, Params, Perbill, SessionConfig, + BalancesConfig, ConsensusConfig, GenesisConfig, IndicesConfig, Params, SessionConfig, SudoConfig, TimestampConfig, XAccountsConfig, XAssetsConfig, XBridgeOfBTCConfig, XFeeManagerConfig, XSpotConfig, XStakingConfig, XSystemConfig, }; @@ -143,10 +143,8 @@ pub fn testnet_genesis(genesis_spec: GenesisSpec) -> GenesisConfig { sessions_per_era: 10, bonding_duration: 10, current_era: 0, - current_offline_slash: 100, - offline_slash_grace: 0, - offline_slash: Perbill::from_millionths(0), - current_session_reward: 100, + penalty: 100, + funding: Default::default(), intentions: vec![], }), xspot: Some(XSpotConfig { diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 1572bbf50faad..0ce5c0b7b4736 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -26,7 +26,6 @@ srml-system = { git = "https://github.com/chainpool/substrate", default-features srml-balances = { git = "https://github.com/chainpool/substrate", default-features = false } srml-timestamp = { git = "https://github.com/chainpool/substrate", default-features = false } srml-consensus = { git = "https://github.com/chainpool/substrate", default-features = false } -srml-aura = { git = "https://github.com/chainpool/substrate", default-features = false } srml-sudo = { git = "https://github.com/chainpool/substrate", default-features = false } srml-indices = { git = "https://github.com/chainpool/substrate", default-features = false } @@ -44,6 +43,7 @@ runtime-api = { path = "../runtime-api", default-features = false } xrml-xsystem = { path = "../xrml/xsystem", default-features = false } xrml-xaccounts = { path = "../xrml/xaccounts", default-features = false } xrml-session = { path = "../xrml/xsession", default-features = false } +xrml-aura = { path = "../xrml/xaura", default-features = false } xrml-grandpa = { path = "../xrml/xgrandpa", default-features = false } # fee xrml-fee-manager = { path = "../xrml/xfee/manager", default-features = false } @@ -86,7 +86,7 @@ std = [ "srml-consensus/std", "xrml-session/std", "xrml-grandpa/std", - "srml-aura/std", + "xrml-aura/std", "srml-sudo/std", "srml-indices/std", diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 202db0bf86137..6f7c815285b9a 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -33,13 +33,13 @@ extern crate sr_std as rstd; // substrate runtime module #[macro_use] extern crate srml_support; -extern crate srml_aura as aura; extern crate srml_balances as balances; extern crate srml_consensus as consensus; extern crate srml_indices as indices; extern crate srml_sudo as sudo; extern crate srml_system as system; extern crate srml_timestamp as timestamp; +extern crate xrml_aura as aura; extern crate xrml_grandpa as grandpa; extern crate xrml_session as session; // unused @@ -182,8 +182,7 @@ impl Convert for SessionKeyConversion { impl session::Trait for Runtime { type ConvertAccountIdToSessionKey = SessionKeyConversion; - // type OnSessionChange = (Staking, grandpa::SyncedAuthorities); - type OnSessionChange = grandpa::SyncedAuthorities; + type OnSessionChange = (XStaking, grandpa::SyncedAuthorities); type Event = Event; } @@ -194,8 +193,7 @@ impl grandpa::Trait for Runtime { } impl aura::Trait for Runtime { - // type HandleReport = aura::StakingSlasher; - type HandleReport = (); + type HandleReport = aura::StakingSlasher; } // bridge diff --git a/runtime/wasm/Cargo.lock b/runtime/wasm/Cargo.lock index d5709b41bc378..3b1b12f053502 100644 --- a/runtime/wasm/Cargo.lock +++ b/runtime/wasm/Cargo.lock @@ -157,7 +157,6 @@ dependencies = [ "sr-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", "sr-std 0.1.0 (git+https://github.com/chainpool/substrate)", "sr-version 0.1.0 (git+https://github.com/chainpool/substrate)", - "srml-aura 0.1.0 (git+https://github.com/chainpool/substrate)", "srml-balances 0.1.0 (git+https://github.com/chainpool/substrate)", "srml-consensus 0.1.0 (git+https://github.com/chainpool/substrate)", "srml-indices 0.1.0 (git+https://github.com/chainpool/substrate)", @@ -170,6 +169,7 @@ dependencies = [ "substrate-finality-grandpa-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", "substrate-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", "xr-primitives 0.4.0", + "xrml-aura 0.1.0", "xrml-bridge-bitcoin 0.4.0", "xrml-fee-manager 0.4.0", "xrml-grandpa 0.1.0", @@ -1295,27 +1295,6 @@ dependencies = [ "sr-std 0.1.0 (git+https://github.com/chainpool/substrate)", ] -[[package]] -name = "srml-aura" -version = "0.1.0" -source = "git+https://github.com/chainpool/substrate#f54f3f564b71aa6d1c8c4a3a5eac184a7fc38f16" -dependencies = [ - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec-derive 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-io 0.1.0 (git+https://github.com/chainpool/substrate)", - "sr-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", - "sr-std 0.1.0 (git+https://github.com/chainpool/substrate)", - "srml-consensus 0.1.0 (git+https://github.com/chainpool/substrate)", - "srml-staking 0.1.0 (git+https://github.com/chainpool/substrate)", - "srml-support 0.1.0 (git+https://github.com/chainpool/substrate)", - "srml-system 0.1.0 (git+https://github.com/chainpool/substrate)", - "srml-timestamp 0.1.0 (git+https://github.com/chainpool/substrate)", - "substrate-inherents 0.1.0 (git+https://github.com/chainpool/substrate)", - "substrate-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", -] - [[package]] name = "srml-balances" version = "0.1.0" @@ -1405,29 +1384,6 @@ dependencies = [ "substrate-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", ] -[[package]] -name = "srml-staking" -version = "0.1.0" -source = "git+https://github.com/chainpool/substrate#f54f3f564b71aa6d1c8c4a3a5eac184a7fc38f16" -dependencies = [ - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec-derive 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-io 0.1.0 (git+https://github.com/chainpool/substrate)", - "sr-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", - "sr-std 0.1.0 (git+https://github.com/chainpool/substrate)", - "srml-balances 0.1.0 (git+https://github.com/chainpool/substrate)", - "srml-consensus 0.1.0 (git+https://github.com/chainpool/substrate)", - "srml-session 0.1.0 (git+https://github.com/chainpool/substrate)", - "srml-support 0.1.0 (git+https://github.com/chainpool/substrate)", - "srml-system 0.1.0 (git+https://github.com/chainpool/substrate)", - "srml-timestamp 0.1.0 (git+https://github.com/chainpool/substrate)", - "substrate-keyring 0.1.0 (git+https://github.com/chainpool/substrate)", - "substrate-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", -] - [[package]] name = "srml-sudo" version = "0.1.0" @@ -2121,6 +2077,26 @@ dependencies = [ "substrate-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", ] +[[package]] +name = "xrml-aura" +version = "0.1.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec-derive 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 0.1.0 (git+https://github.com/chainpool/substrate)", + "sr-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", + "sr-std 0.1.0 (git+https://github.com/chainpool/substrate)", + "srml-consensus 0.1.0 (git+https://github.com/chainpool/substrate)", + "srml-support 0.1.0 (git+https://github.com/chainpool/substrate)", + "srml-system 0.1.0 (git+https://github.com/chainpool/substrate)", + "srml-timestamp 0.1.0 (git+https://github.com/chainpool/substrate)", + "substrate-inherents 0.1.0 (git+https://github.com/chainpool/substrate)", + "substrate-primitives 0.1.0 (git+https://github.com/chainpool/substrate)", + "xrml-mining-staking 0.4.0", +] + [[package]] name = "xrml-bridge-bitcoin" version = "0.4.0" @@ -2569,13 +2545,11 @@ dependencies = [ "checksum sr-std 0.1.0 (git+https://github.com/chainpool/substrate)" = "" "checksum sr-std 0.1.0 (git+https://github.com/chainx-org/sr-std)" = "" "checksum sr-version 0.1.0 (git+https://github.com/chainpool/substrate)" = "" -"checksum srml-aura 0.1.0 (git+https://github.com/chainpool/substrate)" = "" "checksum srml-balances 0.1.0 (git+https://github.com/chainpool/substrate)" = "" "checksum srml-consensus 0.1.0 (git+https://github.com/chainpool/substrate)" = "" "checksum srml-indices 0.1.0 (git+https://github.com/chainpool/substrate)" = "" "checksum srml-metadata 0.1.0 (git+https://github.com/chainpool/substrate)" = "" "checksum srml-session 0.1.0 (git+https://github.com/chainpool/substrate)" = "" -"checksum srml-staking 0.1.0 (git+https://github.com/chainpool/substrate)" = "" "checksum srml-sudo 0.1.0 (git+https://github.com/chainpool/substrate)" = "" "checksum srml-support 0.1.0 (git+https://github.com/chainpool/substrate)" = "" "checksum srml-support-procedural 0.1.0 (git+https://github.com/chainpool/substrate)" = "" diff --git a/runtime/wasm/target/wasm32-unknown-unknown/release/chainx_runtime_wasm.compact.wasm b/runtime/wasm/target/wasm32-unknown-unknown/release/chainx_runtime_wasm.compact.wasm index 6a9152025e20a..1c8aa4ee217de 100644 Binary files a/runtime/wasm/target/wasm32-unknown-unknown/release/chainx_runtime_wasm.compact.wasm and b/runtime/wasm/target/wasm32-unknown-unknown/release/chainx_runtime_wasm.compact.wasm differ diff --git a/xrml/xaura/Cargo.toml b/xrml/xaura/Cargo.toml new file mode 100644 index 0000000000000..a0a116a5af919 --- /dev/null +++ b/xrml/xaura/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "xrml-aura" +version = "0.1.0" +authors = ["Parity Technologies & Chainpool "] + +[dependencies] +hex-literal = "0.1.0" +parity-codec = { version = "2.2", default-features = false } +parity-codec-derive = { version = "2.1", default-features = false } +serde = { version = "1.0", default-features = false } +substrate-primitives = { git = "https://github.com/chainpool/substrate", default-features = false } +substrate-inherents = { git = "https://github.com/chainpool/substrate", default-features = false } +sr-std = { git = "https://github.com/chainpool/substrate", default-features = false } +sr-io = { git = "https://github.com/chainpool/substrate", default-features = false } +sr-primitives = { git = "https://github.com/chainpool/substrate", default-features = false } +srml-support = { git = "https://github.com/chainpool/substrate", default-features = false } +srml-system = { git = "https://github.com/chainpool/substrate", default-features = false } +srml-consensus = { git = "https://github.com/chainpool/substrate", default-features = false } +srml-timestamp = { git = "https://github.com/chainpool/substrate", default-features = false } +xrml-mining-staking = { path = "../xmining/staking", default-features = false } + +[dev-dependencies] +lazy_static = "1.0" +parking_lot = "0.7.1" + +[features] +default = ["std"] +std = [ + "serde/std", + "parity-codec/std", + "parity-codec-derive/std", + "substrate-primitives/std", + "sr-std/std", + "sr-io/std", + "srml-support/std", + "sr-primitives/std", + "srml-system/std", + "srml-consensus/std", + "srml-timestamp/std", + "xrml-mining-staking/std", + "substrate-inherents/std", +] diff --git a/xrml/xaura/src/lib.rs b/xrml/xaura/src/lib.rs new file mode 100644 index 0000000000000..c58d70a7d6afa --- /dev/null +++ b/xrml/xaura/src/lib.rs @@ -0,0 +1,297 @@ +// Copyright 2017-2018 Parity Technologies (UK) Ltd. +// Copyright 2019 Chainpool. +// 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 . + +//! Consensus extension module for Aura consensus. This manages offline reporting. + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate sr_std as rstd; + +#[macro_use] +extern crate parity_codec_derive; +extern crate parity_codec; + +#[macro_use] +extern crate srml_support as runtime_support; + +extern crate sr_primitives as primitives; +extern crate srml_system as system; +pub extern crate srml_timestamp as timestamp; +extern crate substrate_inherents as inherents; +extern crate substrate_primitives; +extern crate xrml_mining_staking as staking; + +#[cfg(test)] +extern crate srml_consensus as consensus; + +#[cfg(test)] +extern crate sr_io as runtime_io; + +#[cfg(test)] +#[macro_use] +extern crate lazy_static; + +#[cfg(test)] +extern crate parking_lot; + +use inherents::{InherentData, InherentIdentifier, MakeFatalError, ProvideInherent, RuntimeString}; +#[cfg(feature = "std")] +use inherents::{InherentDataProviders, ProvideInherentData}; +#[cfg(feature = "std")] +use parity_codec::Decode; +use primitives::traits::{As, Zero}; +use rstd::{prelude::*, result}; +use runtime_support::storage::StorageValue; +use timestamp::OnTimestampSet; +#[cfg(feature = "std")] +use timestamp::TimestampInherentData; + +mod mock; +mod tests; + +/// The aura inherent identifier. +pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"auraslot"; + +/// The type of the aura inherent. +pub type InherentType = u64; + +/// Auxiliary trait to extract aura inherent data. +pub trait AuraInherentData { + /// Get aura inherent data. + fn aura_inherent_data(&self) -> result::Result; + /// Replace aura inherent data. + fn aura_replace_inherent_data(&mut self, new: InherentType); +} + +impl AuraInherentData for InherentData { + fn aura_inherent_data(&self) -> result::Result { + self.get_data(&INHERENT_IDENTIFIER) + .and_then(|r| r.ok_or_else(|| "Aura inherent data not found".into())) + } + + fn aura_replace_inherent_data(&mut self, new: InherentType) { + self.replace_data(INHERENT_IDENTIFIER, &new); + } +} + +/// Provides the slot duration inherent data for `Aura`. +#[cfg(feature = "std")] +pub struct InherentDataProvider { + slot_duration: u64, +} + +#[cfg(feature = "std")] +impl InherentDataProvider { + pub fn new(slot_duration: u64) -> Self { + Self { slot_duration } + } +} + +#[cfg(feature = "std")] +impl ProvideInherentData for InherentDataProvider { + fn on_register(&self, providers: &InherentDataProviders) -> result::Result<(), RuntimeString> { + if !providers.has_provider(×tamp::INHERENT_IDENTIFIER) { + // Add the timestamp inherent data provider, as we require it. + providers.register_provider(timestamp::InherentDataProvider) + } else { + Ok(()) + } + } + + fn inherent_identifier(&self) -> &'static inherents::InherentIdentifier { + &INHERENT_IDENTIFIER + } + + fn provide_inherent_data( + &self, + inherent_data: &mut InherentData, + ) -> result::Result<(), RuntimeString> { + let timestamp = inherent_data.timestamp_inherent_data()?; + let slot_num = timestamp / self.slot_duration; + inherent_data.put_data(INHERENT_IDENTIFIER, &slot_num) + } + + fn error_to_string(&self, error: &[u8]) -> Option { + RuntimeString::decode(&mut &error[..]).map(Into::into) + } +} + +/// Something which can handle Aura consensus reports. +pub trait HandleReport { + fn handle_report(report: AuraReport); +} + +impl HandleReport for () { + fn handle_report(_report: AuraReport) {} +} + +pub trait Trait: timestamp::Trait { + /// The logic for handling reports. + type HandleReport: HandleReport; +} + +decl_storage! { + trait Store for Module as Aura { + // The last timestamp. + LastTimestamp get(last) build(|_| T::Moment::sa(0)): T::Moment; + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { } +} + +/// A report of skipped authorities in aura. +#[derive(Clone, Encode, Decode, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct AuraReport { + // The first skipped slot. + start_slot: usize, + // The number of times authorities were skipped. + skipped: usize, +} + +impl AuraReport { + /// Call the closure with (validator_indices, punishment_count) for each + /// validator to punish. + pub fn punish(&self, validator_count: usize, mut punish_with: F) + where + F: FnMut(usize, usize), + { + let start_slot = self.start_slot % validator_count; + + // the number of times everyone was skipped. + let skipped_all = self.skipped / validator_count; + // the number of validators who were skipped once after that. + let skipped_after = self.skipped % validator_count; + + let iter = (start_slot..validator_count) + .into_iter() + .chain(0..start_slot) + .enumerate(); + + for (rel_index, actual_index) in iter { + let slash_count = skipped_all + + if rel_index < skipped_after { + 1 + } else { + // avoid iterating over all authorities when skipping a couple. + if skipped_all == 0 { + break; + } + 0 + }; + + if slash_count > 0 { + punish_with(actual_index, slash_count); + } + } + } +} + +impl Module { + /// Determine the Aura slot-duration based on the timestamp module configuration. + pub fn slot_duration() -> u64 { + // we double the minimum block-period so each author can always propose within + // the majority of their slot. + >::block_period() + .as_() + .saturating_mul(2) + } + + fn on_timestamp_set(now: T::Moment, slot_duration: T::Moment) { + let last = Self::last(); + ::LastTimestamp::put(now.clone()); + + if last == T::Moment::zero() { + return; + } + + assert!( + slot_duration > T::Moment::zero(), + "Aura slot duration cannot be zero." + ); + + let last_slot = last / slot_duration.clone(); + let first_skipped = last_slot.clone() + T::Moment::sa(1); + let cur_slot = now / slot_duration; + + assert!( + last_slot < cur_slot, + "Only one block may be authored per slot." + ); + if cur_slot == first_skipped { + return; + } + + let slot_to_usize = |slot: T::Moment| slot.as_() as usize; + + let skipped_slots = cur_slot - last_slot - T::Moment::sa(1); + + H::handle_report(AuraReport { + start_slot: slot_to_usize(first_skipped), + skipped: slot_to_usize(skipped_slots), + }) + } +} + +impl OnTimestampSet for Module { + fn on_timestamp_set(moment: T::Moment) { + Self::on_timestamp_set::(moment, T::Moment::sa(Self::slot_duration())) + } +} + +/// A type for performing slashing based on aura reports. +pub struct StakingSlasher(::rstd::marker::PhantomData); + +impl HandleReport for StakingSlasher { + fn handle_report(report: AuraReport) { + let validators = staking::Module::::validators(); + + report.punish(validators.len(), |idx, _| { + let v = validators[idx].clone(); + staking::Module::::on_offline_validator(&v.0); + }); + } +} + +impl ProvideInherent for Module { + type Call = timestamp::Call; + type Error = MakeFatalError; + const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; + + fn create_inherent(_: &InherentData) -> Option { + None + } + + fn check_inherent(call: &Self::Call, data: &InherentData) -> result::Result<(), Self::Error> { + let timestamp = match call { + timestamp::Call::set(ref timestamp) => timestamp.clone(), + _ => return Ok(()), + }; + + let timestamp_based_slot = timestamp.as_() / Self::slot_duration(); + + let seal_slot = data.aura_inherent_data()?; + + if timestamp_based_slot == seal_slot { + Ok(()) + } else { + Err(RuntimeString::from("timestamp set in block doesn't match slot in seal").into()) + } + } +} diff --git a/xrml/xaura/src/mock.rs b/xrml/xaura/src/mock.rs new file mode 100644 index 0000000000000..0cd3238224305 --- /dev/null +++ b/xrml/xaura/src/mock.rs @@ -0,0 +1,94 @@ +// Copyright 2018 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 . + +//! Test utilities + +#![cfg(test)] + +use primitives::{ + testing::{Digest, DigestItem, Header, UintAuthorityId}, + traits::IdentityLookup, + BuildStorage, +}; +use runtime_io; +use substrate_primitives::{Blake2Hasher, H256}; +use {consensus, system, timestamp, Module, Trait}; + +impl_outer_origin! { + pub enum Origin for Test {} +} + +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Test; + +impl consensus::Trait for Test { + type Log = DigestItem; + type SessionKey = UintAuthorityId; + type InherentOfflineReport = (); +} + +impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::primitives::traits::BlakeTwo256; + type Digest = Digest; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = (); + type Log = DigestItem; +} + +impl timestamp::Trait for Test { + type Moment = u64; + type OnTimestampSet = Aura; +} + +impl Trait for Test { + type HandleReport = (); +} + +pub fn new_test_ext(authorities: Vec) -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::::default() + .build_storage() + .unwrap() + .0; + t.extend( + consensus::GenesisConfig:: { + code: vec![], + authorities: authorities + .into_iter() + .map(|a| UintAuthorityId(a)) + .collect(), + } + .build_storage() + .unwrap() + .0, + ); + t.extend( + timestamp::GenesisConfig:: { period: 1 } + .build_storage() + .unwrap() + .0, + ); + t.into() +} + +pub type System = system::Module; +pub type Aura = Module; diff --git a/xrml/xaura/src/tests.rs b/xrml/xaura/src/tests.rs new file mode 100644 index 0000000000000..63f4869fb7593 --- /dev/null +++ b/xrml/xaura/src/tests.rs @@ -0,0 +1,79 @@ +// Copyright 2017-2018 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 . + +//! Tests for the module. + +#![cfg(test)] + +use mock::{new_test_ext, Aura, System}; +use parking_lot::Mutex; +use primitives::traits::Header; +use runtime_io::with_externalities; +use {AuraReport, HandleReport}; + +#[test] +fn aura_report_gets_skipped_correctly() { + let mut report = AuraReport { + start_slot: 0, + skipped: 30, + }; + + let mut validators = vec![0; 10]; + report.punish(10, |idx, count| validators[idx] += count); + + assert_eq!(validators, vec![3; 10]); + + let mut validators = vec![0; 4]; + report.punish(4, |idx, count| validators[idx] += count); + assert_eq!(validators, vec![8, 8, 7, 7]); + + report.start_slot = 2; + report.punish(4, |idx, count| validators[idx] += count); + assert_eq!(validators, vec![15, 15, 15, 15]); +} + +#[test] +fn aura_reports_offline() { + lazy_static! { + static ref SLASH_COUNTS: Mutex> = Mutex::new(vec![0; 4]); + } + + struct HandleTestReport; + impl HandleReport for HandleTestReport { + fn handle_report(report: AuraReport) { + let mut counts = SLASH_COUNTS.lock(); + report.punish(counts.len(), |idx, count| counts[idx] += count); + } + } + + with_externalities(&mut new_test_ext(vec![0, 1, 2, 3]), || { + System::initialise(&1, &Default::default(), &Default::default()); + let slot_duration = Aura::slot_duration(); + + Aura::on_timestamp_set::(5 * slot_duration, slot_duration); + let header = System::finalise(); + + // no slashing when last step was 0. + assert_eq!(SLASH_COUNTS.lock().as_slice(), &[0, 0, 0, 0]); + + System::initialise(&2, &header.hash(), &Default::default()); + Aura::on_timestamp_set::(8 * slot_duration, slot_duration); + let _header = System::finalise(); + + // Steps 6 and 7 were skipped. + assert_eq!(SLASH_COUNTS.lock().as_slice(), &[0, 0, 1, 1]); + }); +} diff --git a/xrml/xmining/staking/src/lib.rs b/xrml/xmining/staking/src/lib.rs index 0d6fb1785d6bf..741aca6241240 100644 --- a/xrml/xmining/staking/src/lib.rs +++ b/xrml/xmining/staking/src/lib.rs @@ -20,7 +20,6 @@ extern crate sr_std as rstd; #[macro_use] extern crate srml_support as runtime_support; extern crate srml_balances as balances; -#[cfg(test)] extern crate srml_consensus as consensus; extern crate srml_system as system; extern crate srml_timestamp as timestamp; @@ -39,10 +38,7 @@ extern crate substrate_primitives; use balances::OnDilution; use codec::{Compact, HasCompact}; use rstd::prelude::*; -use runtime_primitives::{ - traits::{As, Hash, Lookup, StaticLookup, Zero}, - Perbill, -}; +use runtime_primitives::traits::{As, Hash, Lookup, StaticLookup, Zero}; use runtime_support::dispatch::Result; use runtime_support::{StorageMap, StorageValue}; use system::ensure_signed; @@ -352,9 +348,8 @@ decl_module! { } /// Set the offline slash grace period. - fn set_offline_slash_grace(new: Compact) { - let new: u32 = new.into(); - >::put(new); + fn set_penalty(new: T::Balance) { + >::put(new); } } @@ -370,11 +365,9 @@ decl_event!( { /// All validators have been rewarded by the given balance. Reward(Balance, Balance), - /// One validator (and their nominators) has been given a offline-warning (they're still - /// within their grace). The accrued number of slashes is recorded, too. - OfflineWarning(AccountId, u32), /// One validator (and their nominators) has been slashed by the given amount. OfflineSlash(AccountId, Balance), + OfflineValidator(AccountId), Rotation(Vec<(AccountId, u64)>), Register(u32), Unnominate(BlockNumber), @@ -393,10 +386,6 @@ decl_storage! { pub MinimumValidatorCount get(minimum_validator_count) config(): u32 = DEFAULT_MINIMUM_VALIDATOR_COUNT; /// The length of a staking era in sessions. pub SessionsPerEra get(sessions_per_era) config(): T::BlockNumber = T::BlockNumber::sa(1000); - /// Slash, per validator that is taken for the first time they are found to be offline. - pub OfflineSlash get(offline_slash) config(): Perbill = Perbill::from_millionths(1000); // Perbill::from_fraction() is only for std, so use from_millionths(). - /// Number of instances of offline reports before slashing begins for validators. - pub OfflineSlashGrace get(offline_slash_grace) config(): u32; /// The length of the bonding duration in blocks. pub BondingDuration get(bonding_duration) config(): T::BlockNumber = T::BlockNumber::sa(1000); @@ -405,11 +394,6 @@ decl_storage! { /// All the accounts with a desire to stake. pub Intentions get(intentions) config(): Vec; - /// Maximum reward, per validator, that is provided per acceptable session. - pub CurrentSessionReward get(current_session_reward) config(): T::Balance; - /// Slash, per validator that is taken for the first time they are found to be offline. - pub CurrentOfflineSlash get(current_offline_slash) config(): T::Balance; - /// The next value of sessions per era. pub NextSessionsPerEra get(next_sessions_per_era): Option; /// The session index at which the era length last changed. @@ -423,6 +407,10 @@ decl_storage! { pub IntentionProfiles get(intention_profiles): map T::AccountId => IntentionProfs; pub NominationRecords get(nomination_records): map (T::AccountId, T::AccountId) => Option>; + + pub Funding get(funding) config(): T::AccountId; + pub Penalty get(penalty) config(): T::Balance; + pub PunishList get(punish_list): Vec; } } @@ -691,6 +679,10 @@ impl Module { } impl Module { + pub fn validators() -> Vec<(T::AccountId, u64)> { + session::Module::::validators() + } + pub fn jackpot_accountid_for(who: &T::AccountId) -> T::AccountId { T::DetermineJackpotAccountId::accountid_for(who) } diff --git a/xrml/xmining/staking/src/shifter.rs b/xrml/xmining/staking/src/shifter.rs index 02a0ac47995b8..8637ee76508fe 100644 --- a/xrml/xmining/staking/src/shifter.rs +++ b/xrml/xmining/staking/src/shifter.rs @@ -67,6 +67,17 @@ impl Module { let _ = >::pcx_issue(&jackpot_addr, to_jackpot); } + /// Punish a given (potential) validator by a specific amount. + fn punish(who: &T::AccountId, punish: T::Balance) -> bool { + let jackpot_addr = T::DetermineJackpotAccountId::accountid_for(who); + let fund_id = Self::funding(); + if punish <= >::pcx_free_balance(&jackpot_addr) { + let _ = >::pcx_move_free_balance(&jackpot_addr, &fund_id, punish); + return true; + } + return false; + } + /// Session has just changed. We need to determine whether we pay a reward, slash and/or /// move to a new era. fn new_session(_actual_elapsed: T::Moment, should_reward: bool) { @@ -163,22 +174,38 @@ impl Module { let desired_validator_count = >::get() as usize; - let vals = &intentions - .clone() - .into_iter() - .map(|(stake_weight, account_id)| (account_id, stake_weight.as_())) - .take(desired_validator_count) - .collect::>(); + let mut vals = Vec::new(); + let mut count = 0; + let punish_list = >::take(); + for (stake_weight, account_id) in intentions.clone().into_iter() { + let exist = punish_list.iter().any(|account| { + if account.clone() == account_id { + true + } else { + false + } + }); + if exist { + Self::deposit_event(RawEvent::OfflineValidator(account_id.clone())); + } else { + count += 1; + vals.push((account_id.clone(), stake_weight.as_())); + if count == desired_validator_count { + break; + } + } + } - >::set_validators(vals); + >::set_validators(&vals); Self::deposit_event(RawEvent::Rotation(vals.clone())); + } - // Update the balances for slashing/rewarding according to the stakes. - // >::put(Self::offline_slash().times(average_stake)); - // >::put(Self::session_reward().times(average_stake)); - - // Disable slash mechanism - >::put(Self::this_session_reward()); + pub fn on_offline_validator(v: &T::AccountId) { + let penalty = Self::penalty(); + if Self::punish(v, penalty) == false { + >::mutate(|i| i.push(v.clone())); + } + Self::deposit_event(RawEvent::OfflineSlash(v.clone(), penalty)); } } @@ -187,3 +214,12 @@ impl OnSessionChange for Module { Self::new_session(elapsed, should_reward); } } + +impl consensus::OnOfflineReport> for Module { + fn handle_report(reported_indices: Vec) { + for validator_index in reported_indices { + let v = >::validators()[validator_index as usize].clone(); + Self::on_offline_validator(&v.0); + } + } +}