diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index 80260801ef..ddfa241e37 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -58,8 +58,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node"), impl_name: create_runtime_str!("substrate-node"), authoring_version: 10, - spec_version: 39, - impl_version: 39, + spec_version: 40, + impl_version: 40, apis: RUNTIME_API_VERSIONS, }; diff --git a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm index 3af4c1768d..bb36c2cbf7 100644 Binary files a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm and b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm differ diff --git a/substrate/srml/staking/src/lib.rs b/substrate/srml/staking/src/lib.rs index aa3ef1d4b2..68e26a8e63 100644 --- a/substrate/srml/staking/src/lib.rs +++ b/substrate/srml/staking/src/lib.rs @@ -960,56 +960,62 @@ impl Module { /// Call when a validator is determined to be offline. `count` is the /// number of offences the validator has committed. - pub fn on_offline_validator(v: T::AccountId, count: usize) { + /// + /// NOTE: This is called with the controller (not the stash) account id. + pub fn on_offline_validator(controller: T::AccountId, count: usize) { use primitives::traits::CheckedShl; - // Early exit if validator is invulnerable. - if Self::invulnerables().contains(&v) { - return - } + if let Some(l) = Self::ledger(&controller) { + let stash = l.stash; - let slash_count = Self::slash_count(&v); - let new_slash_count = slash_count + count as u32; - >::insert(&v, new_slash_count); - let grace = Self::offline_slash_grace(); + // Early exit if validator is invulnerable. + if Self::invulnerables().contains(&stash) { + return + } - if RECENT_OFFLINE_COUNT > 0 { - let item = (v.clone(), >::block_number(), count as u32); - >::mutate(|v| if v.len() >= RECENT_OFFLINE_COUNT { - let index = v.iter() - .enumerate() - .min_by_key(|(_, (_, block, _))| block) - .expect("v is non-empty; qed") - .0; - v[index] = item; + let slash_count = Self::slash_count(&stash); + let new_slash_count = slash_count + count as u32; + >::insert(&stash, new_slash_count); + let grace = Self::offline_slash_grace(); + + if RECENT_OFFLINE_COUNT > 0 { + let item = (stash.clone(), >::block_number(), count as u32); + >::mutate(|v| if v.len() >= RECENT_OFFLINE_COUNT { + let index = v.iter() + .enumerate() + .min_by_key(|(_, (_, block, _))| block) + .expect("v is non-empty; qed") + .0; + v[index] = item; + } else { + v.push(item); + }); + } + + let prefs = Self::validators(&stash); + let unstake_threshold = prefs.unstake_threshold.min(MAX_UNSTAKE_THRESHOLD); + let max_slashes = grace + unstake_threshold; + + let event = if new_slash_count > max_slashes { + let slash_exposure = Self::stakers(&stash).total; + let offline_slash_base = Self::offline_slash() * slash_exposure; + // They're bailing. + let slash = offline_slash_base + // Multiply slash_mantissa by 2^(unstake_threshold with upper bound) + .checked_shl(unstake_threshold) + .map(|x| x.min(slash_exposure)) + .unwrap_or(slash_exposure); + let _ = Self::slash_validator(&stash, slash); + >::remove(&stash); + let _ = Self::apply_force_new_era(false); + + RawEvent::OfflineSlash(stash.clone(), slash) } else { - v.push(item); - }); + RawEvent::OfflineWarning(stash.clone(), slash_count) + }; + + Self::deposit_event(event); } - - let prefs = Self::validators(&v); - let unstake_threshold = prefs.unstake_threshold.min(MAX_UNSTAKE_THRESHOLD); - let max_slashes = grace + unstake_threshold; - - let event = if new_slash_count > max_slashes { - let slash_exposure = Self::stakers(&v).total; - let offline_slash_base = Self::offline_slash() * slash_exposure; - // They're bailing. - let slash = offline_slash_base - // Multiply slash_mantissa by 2^(unstake_threshold with upper bound) - .checked_shl(unstake_threshold) - .map(|x| x.min(slash_exposure)) - .unwrap_or(slash_exposure); - let _ = Self::slash_validator(&v, slash); - >::remove(&v); - let _ = Self::apply_force_new_era(false); - - RawEvent::OfflineSlash(v.clone(), slash) - } else { - RawEvent::OfflineWarning(v.clone(), slash_count) - }; - - Self::deposit_event(event); } } diff --git a/substrate/srml/staking/src/tests.rs b/substrate/srml/staking/src/tests.rs index 50c5772985..86b583fab8 100644 --- a/substrate/srml/staking/src/tests.rs +++ b/substrate/srml/staking/src/tests.rs @@ -117,7 +117,7 @@ fn invulnerability_should_work() { // Set account 11 as an offline validator with a large number of reports // Should exit early if invulnerable - Staking::on_offline_validator(11, 100); + Staking::on_offline_validator(10, 100); // Show that account 11 has not been touched assert_eq!(Staking::slash_count(&11), 0); @@ -146,7 +146,7 @@ fn offline_should_slash_and_kick() { // Account 10 has the funds we just gave it assert_eq!(Balances::free_balance(&11), 1000); // Report account 10 as offline, one greater than unstake threshold - Staking::on_offline_validator(11, 4); + Staking::on_offline_validator(10, 4); // Confirm user has been reported assert_eq!(Staking::slash_count(&11), 4); // Confirm balance has been reduced by 2^unstake_threshold * offline_slash() * amount_at_stake. @@ -181,7 +181,7 @@ fn offline_grace_should_delay_slashing() { assert_eq!(Staking::slash_count(&11), 0); // Report account 10 up to the threshold - Staking::on_offline_validator(11, default_unstake_threshold as usize + offline_slash_grace as usize); + Staking::on_offline_validator(10, default_unstake_threshold as usize + offline_slash_grace as usize); // Confirm slash count assert_eq!(Staking::slash_count(&11), 4); @@ -189,7 +189,7 @@ fn offline_grace_should_delay_slashing() { assert_eq!(Balances::free_balance(&11), 70); // Report account 10 one more time - Staking::on_offline_validator(11, 1); + Staking::on_offline_validator(10, 1); assert_eq!(Staking::slash_count(&11), 5); // User gets slashed assert_eq!(Balances::free_balance(&11), 0); @@ -236,8 +236,8 @@ fn max_unstake_threshold_works() { >::put(Perbill::from_fraction(0.0001)); // Report each user 1 more than the max_unstake_threshold - Staking::on_offline_validator(11, MAX_UNSTAKE_THRESHOLD as usize + 1); - Staking::on_offline_validator(21, MAX_UNSTAKE_THRESHOLD as usize + 1); + Staking::on_offline_validator(10, MAX_UNSTAKE_THRESHOLD as usize + 1); + Staking::on_offline_validator(20, MAX_UNSTAKE_THRESHOLD as usize + 1); // Show that each balance only gets reduced by 2^max_unstake_threshold times 10% // of their total stake. @@ -264,7 +264,7 @@ fn slashing_does_not_cause_underflow() { Session::check_rotate_session(System::block_number()); // Should not panic - Staking::on_offline_validator(11, 100); + Staking::on_offline_validator(10, 100); // Confirm that underflow has not occurred, and account balance is set to zero assert_eq!(Balances::free_balance(&11), 0); }); @@ -1261,7 +1261,7 @@ fn slot_stake_is_least_staked_validator_and_limits_maximum_punishment() { assert_eq!(Staking::slot_stake(), 79); // // If 10 gets slashed now, despite having +1000 in stash, it will be slashed byt 79, which is the slot stake - Staking::on_offline_validator(11, 4); + Staking::on_offline_validator(10, 4); // // Confirm user has been reported assert_eq!(Staking::slash_count(&11), 4); // // check the balance of 10 (slash will be deducted from free balance.) @@ -1288,7 +1288,7 @@ fn on_free_balance_zero_stash_removes_validator() { // Set some storage items which we expect to be cleaned up // Initiate slash count storage item - Staking::on_offline_validator(11, 1); + Staking::on_offline_validator(10, 1); // Set payee information assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Stash));