Repository: paritytech/substrate (rust)

Issue:
broken benchmark for pallet_offences (report_offence_im_online)
I want to run all possible benchmarks on FRAME.

I have `substrate` checked out at `monthly-2021-10`. While running the benchmarks on my machine (i7 CPU, 16Gb RAM), I got stuck with the following error:

```
$ ./target/release/substrate benchmark --chain dev --steps 50 --repeat 20 --pallet=* --extrinsic=*
...
2021-10-13 01:49:58 Running Benchmark:	pallet_offences	report_offence_im_online	214/50	0/1    
2021-10-13 01:50:10 Running Benchmark:	pallet_offences	report_offence_im_online	215/50	0/1    

====================

Version: 3.0.0-dev-bf9683eee4-x86_64-linux-gnu

   0: sp_panic_handler::set::{{closure}}
   1: std::panicking::rust_panic_with_hook
             at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panicking.rs:626:17
   2: std::panicking::begin_panic_handler::{{closure}}
             at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panicking.rs:519:13
   3: std::sys_common::backtrace::__rust_end_short_backtrace
             at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/sys_common/backtrace.rs:141:18
   4: rust_begin_unwind
             at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panicking.rs:515:5
   5: core::panicking::panic_fmt
             at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/core/src/panicking.rs:92:14
   6: core::panicking::assert_failed_inner
   7: core::panicking::assert_failed
   8: core::ops::function::FnOnce::call_once{{vtable.shim}}
   9: <pallet_offences_benchmarking::Pallet<T> as frame_benchmarking::utils::Benchmarking>::run_benchmark
  10: <node_runtime::Runtime as frame_benchmarking::utils::runtime_decl_for_Benchmark::Benchmark<sp_runtime::generic::block::Block<sp_runtime::generic::header::Header<u32,sp_runtime::traits::BlakeTwo256>,sp_runtime::generic::unchecked_extrinsic::UncheckedExtrinsic<sp_runtime::multiaddress::MultiAddress<<<sp_runtime::MultiSignature as sp_runtime::traits::Verify>::Signer as sp_runtime::traits::IdentifyAccount>::AccountId,u32>,node_runtime::Call,sp_runtime::MultiSignature,(frame_system::extensions::check_spec_version::CheckSpecVersion<node_runtime::Runtime>,frame_system::extensions::check_tx_version::CheckTxVersion<node_runtime::Runtime>,frame_system::extensions::check_genesis::CheckGenesis<node_runtime::Runtime>,frame_system::extensions::check_mortality::CheckMortality<node_runtime::Runtime>,frame_system::extensions::check_nonce::CheckNonce<node_runtime::Runtime>,frame_system::extensions::check_weight::CheckWeight<node_runtime::Runtime>,pallet_transaction_payment::ChargeTransactionPayment<node_runtime::Runtime>)>>>>::dispatch_benchmark
  11: node_runtime::api::dispatch
  12: <node_executor::ExecutorDispatch as sc_executor::native_executor::NativeExecutionDispatch>::dispatch
  13: std::thread::local::LocalKey<T>::with
  14: sc_executor::native_executor::WasmExecutor::with_instance::{{closure}}
  15: sc_executor::wasm_runtime::RuntimeCache::with_instance
  16: <sc_executor::native_executor::NativeElseWasmExecutor<D> as sp_core::traits::CodeExecutor>::call
  17: sp_state_machine::execution::StateMachine<B,H,N,Exec>::execute_aux
  18: sp_state_machine::execution::StateMachine<B,H,N,Exec>::execute
  19: frame_benchmarking_cli::command::<impl frame_benchmarking_cli::BenchmarkCmd>::run
  20: node_cli::command::run
  21: substrate::main
  22: std::sys_common::backtrace::__rust_begin_short_backtrace
  23: std::rt::lang_start::{{closure}}
  24: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once
             at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/core/src/ops/function.rs:259:13
      std::panicking::try::do_call
             at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panicking.rs:401:40
      std::panicking::try
             at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panicking.rs:365:19
      std::panic::catch_unwind
             at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panic.rs:434:14
      std::rt::lang_start_internal::{{closure}}
             at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/rt.rs:45:48
      std::panicking::try::do_call
             at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panicking.rs:401:40
      std::panicking::try
             at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panicking.rs:365:19
      std::panic::catch_unwind
             at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/panic.rs:434:14
      std::rt::lang_start_internal
             at /rustc/c8dfcfe046a7680554bf4eb612bad840e7631c4b/library/std/src/rt.rs:45:20
  25: main
  26: __libc_start_main
  27: _start


Thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `2`,
 right: `5`', /home/bear/develop/parity/substrate/substrate/frame/offences/benchmarking/src/lib.rs:358

This is a bug. Please report it at:

	https://github.com/paritytech/substrate/issues/new

2021-10-13 01:50:11 Evicting failed runtime instance: RuntimePanicked("assertion failed: `(left == right)`\n  left: `2`,\n right: `5`")    
Error: Input("Error executing and verifying runtime benchmark: RuntimePanicked(\"assertion failed: `(left == right)`\\n  left: `2`,\\n right: `5`\")")
```

Is this an actual bug, or did I do something wrong when I started the benchmarking?

Pull request:
Make `offences` benchmark runnable
The `pallet_offences` benchmarks currently panic. Fixes #10027 until #10781 is done.  
The outputs of the benchmarks are currently unused. I will call the benchbot and revert to make sure that they do not panic anymore.

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index af6cb045f7..2e254e7d11 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -9738,7 +9738,7 @@ dependencies = [
 
 [[package]]
 name = "sp-keyring"
-version = "4.1.0-dev"
+version = "5.0.0"
 dependencies = [
  "lazy_static",
  "sp-core",
diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml
index db198592b3..211311c3da 100644
--- a/bin/node/cli/Cargo.toml
+++ b/bin/node/cli/Cargo.toml
@@ -52,7 +52,7 @@ sp-runtime = { version = "5.0.0", path = "../../../primitives/runtime" }
 sp-timestamp = { version = "4.0.0-dev", path = "../../../primitives/timestamp" }
 sp-authorship = { version = "4.0.0-dev", path = "../../../primitives/authorship" }
 sp-inherents = { version = "4.0.0-dev", path = "../../../primitives/inherents" }
-sp-keyring = { version = "4.1.0-dev", path = "../../../primitives/keyring" }
+sp-keyring = { version = "5.0.0", path = "../../../primitives/keyring" }
 sp-keystore = { version = "0.11.0", path = "../../../primitives/keystore" }
 sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" }
 sp-transaction-pool = { version = "4.0.0-dev", path = "../../../primitives/transaction-pool" }
diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml
index 39cf1ee8d8..531fffe355 100644
--- a/bin/node/executor/Cargo.toml
+++ b/bin/node/executor/Cargo.toml
@@ -38,7 +38,7 @@ sp-application-crypto = { version = "5.0.0", path = "../../../primitives/applica
 sp-consensus-babe = { version = "0.10.0-dev", path = "../../../primitives/consensus/babe" }
 sp-runtime = { version = "5.0.0", path = "../../../primitives/runtime" }
 sp-externalities = { version = "0.11.0", path = "../../../primitives/externalities" }
-sp-keyring = { version = "4.1.0-dev", path = "../../../primitives/keyring" }
+sp-keyring = { version = "5.0.0", path = "../../../primitives/keyring" }
 wat = "1.0"
 futures = "0.3.9"
 
diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml
index 84eec76623..647562caff 100644
--- a/bin/node/runtime/Cargo.toml
+++ b/bin/node/runtime/Cargo.toml
@@ -35,7 +35,7 @@ sp-std = { version = "4.0.0", default-features = false, path = "../../../primiti
 sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" }
 sp-runtime = { version = "5.0.0", default-features = false, path = "../../../primitives/runtime" }
 sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/staking" }
-sp-keyring = { version = "4.1.0-dev", optional = true, path = "../../../primitives/keyring" }
+sp-keyring = { version = "5.0.0", optional = true, path = "../../../primitives/keyring" }
 sp-session = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/session" }
 sp-transaction-pool = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/transaction-pool" }
 sp-version = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/version" }
diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml
index ca7e0ac768..f7af649d3b 100644
--- a/client/cli/Cargo.toml
+++ b/client/cli/Cargo.toml
@@ -40,7 +40,7 @@ sc-tracing = { version = "4.0.0-dev", path = "../tracing" }
 sc-utils = { version = "4.0.0-dev", path = "../utils" }
 sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" }
 sp-core = { version = "5.0.0", path = "../../primitives/core" }
-sp-keyring = { version = "4.1.0-dev", path = "../../primitives/keyring" }
+sp-keyring = { version = "5.0.0", path = "../../primitives/keyring" }
 sp-keystore = { version = "0.11.0", path = "../../primitives/keystore" }
 sp-panic-handler = { version = "4.0.0", path = "../../primitives/panic-handler" }
 sp-runtime = { version = "5.0.0", path = "../../primitives/runtime" }
diff --git a/client/consensus/aura/Cargo.toml b/client/consensus/aura/Cargo.toml
index 4fb621d716..156842c358 100644
--- a/client/consensus/aura/Cargo.toml
+++ b/client/consensus/aura/Cargo.toml
@@ -41,7 +41,7 @@ getrandom = { version = "0.2", features = ["js"], optional = true }
 
 [dev-dependencies]
 sp-timestamp = { version = "4.0.0-dev", path = "../../../primitives/timestamp" }
-sp-keyring = { version = "4.1.0-dev", path = "../../../primitives/keyring" }
+sp-keyring = { version = "5.0.0", path = "../../../primitives/keyring" }
 sp-tracing = { version = "4.0.0", path = "../../../primitives/tracing" }
 sc-keystore = { version = "4.0.0-dev", path = "../../keystore" }
 sc-network = { version = "0.10.0-dev", path = "../../network" }
diff --git a/client/consensus/babe/rpc/Cargo.toml b/client/consensus/babe/rpc/Cargo.toml
index 87ef265ffd..56276f07dc 100644
--- a/client/consensus/babe/rpc/Cargo.toml
+++ b/client/consensus/babe/rpc/Cargo.toml
@@ -34,7 +34,7 @@ sp-keystore = { version = "0.11.0", path = "../../../../primitives/keystore" }
 [dev-dependencies]
 sc-consensus = { version = "0.10.0-dev", path = "../../../consensus/common" }
 serde_json = "1.0.74"
-sp-keyring = { version = "4.1.0-dev", path = "../../../../primitives/keyring" }
+sp-keyring = { version = "5.0.0", path = "../../../../primitives/keyring" }
 sc-keystore = { version = "4.0.0-dev", path = "../../../keystore" }
 substrate-test-runtime-client = { version = "2.0.0", path = "../../../../test-utils/runtime/client" }
 tempfile = "3.1.0"
diff --git a/client/finality-grandpa/Cargo.toml b/client/finality-grandpa/Cargo.toml
index e6b0b63966..6d4c3b1366 100644
--- a/client/finality-grandpa/Cargo.toml
+++ b/client/finality-grandpa/Cargo.toml
@@ -54,7 +54,7 @@ finality-grandpa = { version = "0.14.1", features = [
 ] }
 sc-network = { version = "0.10.0-dev", path = "../network" }
 sc-network-test = { version = "0.8.0", path = "../network/test" }
-sp-keyring = { version = "4.1.0-dev", path = "../../primitives/keyring" }
+sp-keyring = { version = "5.0.0", path = "../../primitives/keyring" }
 substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" }
 sp-tracing = { version = "4.0.0", path = "../../primitives/tracing" }
 tokio = "1.15"
diff --git a/client/finality-grandpa/rpc/Cargo.toml b/client/finality-grandpa/rpc/Cargo.toml
index 2845621cad..b544287a60 100644
--- a/client/finality-grandpa/rpc/Cargo.toml
+++ b/client/finality-grandpa/rpc/Cargo.toml
@@ -34,5 +34,5 @@ sc-rpc = { version = "4.0.0-dev", path = "../../rpc", features = [
 ] }
 sp-core = { version = "5.0.0", path = "../../../primitives/core" }
 sp-finality-grandpa = { version = "4.0.0-dev", path = "../../../primitives/finality-grandpa" }
-sp-keyring = { version = "4.1.0-dev", path = "../../../primitives/keyring" }
+sp-keyring = { version = "5.0.0", path = "../../../primitives/keyring" }
 substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" }
diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs
index 0a620e6c12..a5ed593e1a 100644
--- a/frame/election-provider-multi-phase/src/lib.rs
+++ b/frame/election-provider-multi-phase/src/lib.rs
@@ -255,7 +255,7 @@ use sp_runtime::{
 		InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity,
 		TransactionValidityError, ValidTransaction,
 	},
-	DispatchError, PerThing, Perbill, RuntimeDebug, SaturatedConversion,
+	DispatchError, ModuleError, PerThing, Perbill, RuntimeDebug, SaturatedConversion,
 };
 use sp_std::prelude::*;
 
@@ -1579,7 +1579,7 @@ impl<T: Config> ElectionProvider for Pallet<T> {
 /// number.
 pub fn dispatch_error_to_invalid(error: DispatchError) -> InvalidTransaction {
 	let error_number = match error {
-		DispatchError::Module { error, .. } => error,
+		DispatchError::Module(ModuleError { error, .. }) => error,
 		_ => 0,
 	};
 	InvalidTransaction::Custom(error_number)
diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs
index 936993b41f..196147f8a4 100644
--- a/frame/election-provider-multi-phase/src/unsigned.rs
+++ b/frame/election-provider-multi-phase/src/unsigned.rs
@@ -752,7 +752,7 @@ mod tests {
 	use sp_runtime::{
 		offchain::storage_lock::{BlockAndTime, StorageLock},
 		traits::ValidateUnsigned,
-		PerU16,
+		ModuleError, PerU16,
 	};
 
 	type Assignment = crate::unsigned::Assignment<Runtime>;
@@ -922,8 +922,8 @@ mod tests {
 	#[test]
 	#[should_panic(expected = "Invalid unsigned submission must produce invalid block and \
 	                           deprive validator from their authoring reward.: \
-	                           Module { index: 2, error: 1, message: \
-	                           Some(\"PreDispatchWrongWinnerCount\") }")]
+	                           Module(ModuleError { index: 2, error: 1, message: \
+	                           Some(\"PreDispatchWrongWinnerCount\") })")]
 	fn unfeasible_solution_panics() {
 		ExtBuilder::default().build_and_execute(|| {
 			roll_to(25);
@@ -1033,11 +1033,11 @@ mod tests {
 
 			assert_eq!(
 				MultiPhase::mine_check_save_submit().unwrap_err(),
-				MinerError::PreDispatchChecksFailed(DispatchError::Module {
+				MinerError::PreDispatchChecksFailed(DispatchError::Module(ModuleError {
 					index: 2,
 					error: 1,
 					message: Some("PreDispatchWrongWinnerCount"),
-				}),
+				})),
 			);
 		})
 	}
diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs
index bc6a81125e..4758c793cf 100644
--- a/frame/elections-phragmen/src/lib.rs
+++ b/frame/elections-phragmen/src/lib.rs
@@ -1132,7 +1132,7 @@ mod tests {
 	use sp_runtime::{
 		testing::Header,
 		traits::{BlakeTwo256, IdentityLookup},
-		BuildStorage,
+		BuildStorage, ModuleError,
 	};
 	use substrate_test_utils::assert_eq_uvec;
 
@@ -2514,7 +2514,7 @@ mod tests {
 			let unwrapped_error = Elections::remove_member(Origin::root(), 4, true).unwrap_err();
 			assert!(matches!(
 				unwrapped_error.error,
-				DispatchError::Module { message: Some("InvalidReplacement"), .. }
+				DispatchError::Module(ModuleError { message: Some("InvalidReplacement"), .. })
 			));
 			assert!(unwrapped_error.post_info.actual_weight.is_some());
 		});
@@ -2537,7 +2537,7 @@ mod tests {
 			let unwrapped_error = Elections::remove_member(Origin::root(), 4, false).unwrap_err();
 			assert!(matches!(
 				unwrapped_error.error,
-				DispatchError::Module { message: Some("InvalidReplacement"), .. }
+				DispatchError::Module(ModuleError { message: Some("InvalidReplacement"), .. })
 			));
 			assert!(unwrapped_error.post_info.actual_weight.is_some());
 		});
diff --git a/frame/grandpa/Cargo.toml b/frame/grandpa/Cargo.toml
index 002ceed369..eda9d630d4 100644
--- a/frame/grandpa/Cargo.toml
+++ b/frame/grandpa/Cargo.toml
@@ -33,7 +33,7 @@ log = { version = "0.4.14", default-features = false }
 [dev-dependencies]
 frame-benchmarking = { version = "4.0.0-dev", path = "../benchmarking" }
 grandpa = { package = "finality-grandpa", version = "0.14.1", features = ["derive-codec"] }
-sp-keyring = { version = "4.1.0-dev", path = "../../primitives/keyring" }
+sp-keyring = { version = "5.0.0", path = "../../primitives/keyring" }
 pallet-balances = { version = "4.0.0-dev", path = "../balances" }
 pallet-offences = { version = "4.0.0-dev", path = "../offences" }
 pallet-staking = { version = "4.0.0-dev", path = "../staking" }
diff --git a/frame/indices/Cargo.toml b/frame/indices/Cargo.toml
index 487eb47164..28a6427f52 100644
--- a/frame/indices/Cargo.toml
+++ b/frame/indices/Cargo.toml
@@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 [dependencies]
 codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
 scale-info = { version = "1.0", default-features = false, features = ["derive"] }
-sp-keyring = { version = "4.1.0-dev", optional = true, path = "../../primitives/keyring" }
+sp-keyring = { version = "5.0.0", optional = true, path = "../../primitives/keyring" }
 sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" }
 sp-io = { version = "5.0.0", default-features = false, path = "../../primitives/io" }
 sp-runtime = { version = "5.0.0", default-features = false, path = "../../primitives/runtime" }
diff --git a/frame/offences/benchmarking/src/lib.rs b/frame/offences/benchmarking/src/lib.rs
index c2e29f0d0f..8688090206 100644
--- a/frame/offences/benchmarking/src/lib.rs
+++ b/frame/offences/benchmarking/src/lib.rs
@@ -405,6 +405,7 @@ benchmarks! {
 	}
 	verify {
 		// make sure that all slashes have been applied
+		#[cfg(test)]
 		assert_eq!(
 			System::<T>::event_count(), 0
 			+ 1 // offence
@@ -440,6 +441,7 @@ benchmarks! {
 	}
 	verify {
 		// make sure that all slashes have been applied
+		#[cfg(test)]
 		assert_eq!(
 			System::<T>::event_count(), 0
 			+ 1 // offence
diff --git a/frame/support/procedural/src/pallet/expand/error.rs b/frame/support/procedural/src/pallet/expand/error.rs
index 4cf572c179..9e2b801083 100644
--- a/frame/support/procedural/src/pallet/expand/error.rs
+++ b/frame/support/procedural/src/pallet/expand/error.rs
@@ -124,11 +124,11 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream {
 				>::index::<Pallet<#type_use_gen>>()
 					.expect("Every active module has an index in the runtime; qed") as u8;
 
-				#frame_support::sp_runtime::DispatchError::Module {
+				#frame_support::sp_runtime::DispatchError::Module(#frame_support::sp_runtime::ModuleError {
 					index,
 					error: err.as_u8(),
 					message: Some(err.as_str()),
-				}
+				})
 			}
 		}
 	)
diff --git a/frame/support/src/error.rs b/frame/support/src/error.rs
index abac4a5326..4880bba5c5 100644
--- a/frame/support/src/error.rs
+++ b/frame/support/src/error.rs
@@ -153,11 +153,11 @@ macro_rules! decl_error {
 					::index::<$module<$generic $(, $inst_generic)?>>()
 					.expect("Every active module has an index in the runtime; qed") as u8;
 
-				$crate::sp_runtime::DispatchError::Module {
+				$crate::sp_runtime::DispatchError::Module($crate::sp_runtime::ModuleError {
 					index,
 					error: err.as_u8(),
 					message: Some(err.as_str()),
-				}
+				})
 			}
 		}
 	};
diff --git a/primitives/keyring/Cargo.toml b/primitives/keyring/Cargo.toml
index 2f8e935b24..5cf8a57450 100644
--- a/primitives/keyring/Cargo.toml
+++ b/primitives/keyring/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "sp-keyring"
-version = "4.1.0-dev"
+version = "5.0.0"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2021"
 license = "Apache-2.0"
diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs
index 84817a6be5..6d32d2322c 100644
--- a/primitives/runtime/src/lib.rs
+++ b/primitives/runtime/src/lib.rs
@@ -461,9 +461,29 @@ pub type DispatchResult = sp_std::result::Result<(), DispatchError>;
 /// about the `Dispatchable` that is only known post dispatch.
 pub type DispatchResultWithInfo<T> = sp_std::result::Result<T, DispatchErrorWithPostInfo<T>>;
 
-/// Reason why a dispatch call failed.
+/// Reason why a pallet call failed.
 #[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo)]
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct ModuleError {
+	/// Module index, matching the metadata module index.
+	pub index: u8,
+	/// Module specific error value.
+	pub error: u8,
+	/// Optional error message.
+	#[codec(skip)]
+	#[cfg_attr(feature = "std", serde(skip_deserializing))]
+	pub message: Option<&'static str>,
+}
+
+impl PartialEq for ModuleError {
+	fn eq(&self, other: &Self) -> bool {
+		(self.index == other.index) && (self.error == other.error)
+	}
+}
+
+/// Reason why a dispatch call failed.
+#[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 pub enum DispatchError {
 	/// Some error occurred.
 	Other(
@@ -476,16 +496,7 @@ pub enum DispatchError {
 	/// A bad origin.
 	BadOrigin,
 	/// A custom error in a module.
-	Module {
-		/// Module index, matching the metadata module index.
-		index: u8,
-		/// Module specific error value.
-		error: u8,
-		/// Optional error message.
-		#[codec(skip)]
-		#[cfg_attr(feature = "std", serde(skip_deserializing))]
-		message: Option<&'static str>,
-	},
+	Module(ModuleError),
 	/// At least one consumer is remaining so the account cannot be destroyed.
 	ConsumerRemaining,
 	/// There are no providers so the account cannot be created.
@@ -515,8 +526,8 @@ impl DispatchError {
 	/// Return the same error but without the attached message.
 	pub fn stripped(self) -> Self {
 		match self {
-			DispatchError::Module { index, error, message: Some(_) } =>
-				DispatchError::Module { index, error, message: None },
+			DispatchError::Module(ModuleError { index, error, message: Some(_) }) =>
+				DispatchError::Module(ModuleError { index, error, message: None }),
 			m => m,
 		}
 	}
@@ -624,7 +635,8 @@ impl From<DispatchError> for &'static str {
 			DispatchError::Other(msg) => msg,
 			DispatchError::CannotLookup => "Cannot lookup",
 			DispatchError::BadOrigin => "Bad origin",
-			DispatchError::Module { message, .. } => message.unwrap_or("Unknown module error"),
+			DispatchError::Module(ModuleError { message, .. }) =>
+				message.unwrap_or("Unknown module error"),
 			DispatchError::ConsumerRemaining => "Consumer remaining",
 			DispatchError::NoProviders => "No providers",
 			DispatchError::TooManyConsumers => "Too many consumers",
@@ -650,7 +662,7 @@ impl traits::Printable for DispatchError {
 			Self::Other(err) => err.print(),
 			Self::CannotLookup => "Cannot lookup".print(),
 			Self::BadOrigin => "Bad origin".print(),
-			Self::Module { index, error, message } => {
+			Self::Module(ModuleError { index, error, message }) => {
 				index.print();
 				error.print();
 				if let Some(msg) = message {
@@ -683,30 +695,6 @@ where
 	}
 }
 
-impl PartialEq for DispatchError {
-	fn eq(&self, other: &Self) -> bool {
-		use DispatchError::*;
-
-		match (self, other) {
-			(CannotLookup, CannotLookup) |
-			(BadOrigin, BadOrigin) |
-			(ConsumerRemaining, ConsumerRemaining) |
-			(NoProviders, NoProviders) => true,
-
-			(Token(l), Token(r)) => l == r,
-			(Other(l), Other(r)) => l == r,
-			(Arithmetic(l), Arithmetic(r)) => l == r,
-
-			(
-				Module { index: index_l, error: error_l, .. },
-				Module { index: index_r, error: error_r, .. },
-			) => (index_l == index_r) && (error_l == error_r),
-
-			_ => false,
-		}
-	}
-}
-
 /// This type specifies the outcome of dispatching a call to a module.
 ///
 /// In case of failure an error specific to the module is returned.
@@ -932,11 +920,18 @@ mod tests {
 
 	#[test]
 	fn dispatch_error_encoding() {
-		let error = DispatchError::Module { index: 1, error: 2, message: Some("error message") };
+		let error = DispatchError::Module(ModuleError {
+			index: 1,
+			error: 2,
+			message: Some("error message"),
+		});
 		let encoded = error.encode();
 		let decoded = DispatchError::decode(&mut &encoded[..]).unwrap();
 		assert_eq!(encoded, vec![3, 1, 2]);
-		assert_eq!(decoded, DispatchError::Module { index: 1, error: 2, message: None });
+		assert_eq!(
+			decoded,
+			DispatchError::Module(ModuleError { index: 1, error: 2, message: None })
+		);
 	}
 
 	#[test]
@@ -948,9 +943,9 @@ mod tests {
 			Other("bar"),
 			CannotLookup,
 			BadOrigin,
-			Module { index: 1, error: 1, message: None },
-			Module { index: 1, error: 2, message: None },
-			Module { index: 2, error: 1, message: None },
+			Module(ModuleError { index: 1, error: 1, message: None }),
+			Module(ModuleError { index: 1, error: 2, message: None }),
+			Module(ModuleError { index: 2, error: 1, message: None }),
 			ConsumerRemaining,
 			NoProviders,
 			Token(TokenError::NoFunds),
@@ -975,8 +970,8 @@ mod tests {
 
 		// Ignores `message` field in `Module` variant.
 		assert_eq!(
-			Module { index: 1, error: 1, message: Some("foo") },
-			Module { index: 1, error: 1, message: None },
+			Module(ModuleError { index: 1, error: 1, message: Some("foo") }),
+			Module(ModuleError { index: 1, error: 1, message: None }),
 		);
 	}
 


Test patch:
diff --git a/bin/node/testing/Cargo.toml b/bin/node/testing/Cargo.toml
index 55ae52b50d..3275aac0ef 100644
--- a/bin/node/testing/Cargo.toml
+++ b/bin/node/testing/Cargo.toml
@@ -24,7 +24,7 @@ sc-client-db = { version = "0.10.0-dev", path = "../../../client/db/", features
 sc-client-api = { version = "4.0.0-dev", path = "../../../client/api/" }
 sc-consensus = { version = "0.10.0-dev", path = "../../../client/consensus/common" }
 codec = { package = "parity-scale-codec", version = "2.0.0" }
-sp-keyring = { version = "4.1.0-dev", path = "../../../primitives/keyring" }
+sp-keyring = { version = "5.0.0", path = "../../../primitives/keyring" }
 node-executor = { version = "3.0.0-dev", path = "../executor" }
 node-primitives = { version = "2.0.0", path = "../primitives" }
 node-runtime = { version = "3.0.0-dev", path = "../runtime" }
diff --git a/frame/support/test/tests/construct_runtime.rs b/frame/support/test/tests/construct_runtime.rs
index 267d560edf..b3f8feb8aa 100644
--- a/frame/support/test/tests/construct_runtime.rs
+++ b/frame/support/test/tests/construct_runtime.rs
@@ -27,7 +27,7 @@ use sp_core::{sr25519, H256};
 use sp_runtime::{
 	generic,
 	traits::{BlakeTwo256, Verify},
-	DispatchError,
+	DispatchError, ModuleError,
 };
 use sp_std::cell::RefCell;
 
@@ -363,47 +363,47 @@ mod origin_test {
 fn check_modules_error_type() {
 	assert_eq!(
 		Module1_1::fail(system::Origin::<Runtime>::Root.into()),
-		Err(DispatchError::Module { index: 31, error: 0, message: Some("Something") }),
+		Err(DispatchError::Module(ModuleError { index: 31, error: 0, message: Some("Something") })),
 	);
 	assert_eq!(
 		Module2::fail(system::Origin::<Runtime>::Root.into()),
-		Err(DispatchError::Module { index: 32, error: 0, message: Some("Something") }),
+		Err(DispatchError::Module(ModuleError { index: 32, error: 0, message: Some("Something") })),
 	);
 	assert_eq!(
 		Module1_2::fail(system::Origin::<Runtime>::Root.into()),
-		Err(DispatchError::Module { index: 33, error: 0, message: Some("Something") }),
+		Err(DispatchError::Module(ModuleError { index: 33, error: 0, message: Some("Something") })),
 	);
 	assert_eq!(
 		NestedModule3::fail(system::Origin::<Runtime>::Root.into()),
-		Err(DispatchError::Module { index: 34, error: 0, message: Some("Something") }),
+		Err(DispatchError::Module(ModuleError { index: 34, error: 0, message: Some("Something") })),
 	);
 	assert_eq!(
 		Module1_3::fail(system::Origin::<Runtime>::Root.into()),
-		Err(DispatchError::Module { index: 6, error: 0, message: Some("Something") }),
+		Err(DispatchError::Module(ModuleError { index: 6, error: 0, message: Some("Something") })),
 	);
 	assert_eq!(
 		Module1_4::fail(system::Origin::<Runtime>::Root.into()),
-		Err(DispatchError::Module { index: 3, error: 0, message: Some("Something") }),
+		Err(DispatchError::Module(ModuleError { index: 3, error: 0, message: Some("Something") })),
 	);
 	assert_eq!(
 		Module1_5::fail(system::Origin::<Runtime>::Root.into()),
-		Err(DispatchError::Module { index: 4, error: 0, message: Some("Something") }),
+		Err(DispatchError::Module(ModuleError { index: 4, error: 0, message: Some("Something") })),
 	);
 	assert_eq!(
 		Module1_6::fail(system::Origin::<Runtime>::Root.into()),
-		Err(DispatchError::Module { index: 1, error: 0, message: Some("Something") }),
+		Err(DispatchError::Module(ModuleError { index: 1, error: 0, message: Some("Something") })),
 	);
 	assert_eq!(
 		Module1_7::fail(system::Origin::<Runtime>::Root.into()),
-		Err(DispatchError::Module { index: 2, error: 0, message: Some("Something") }),
+		Err(DispatchError::Module(ModuleError { index: 2, error: 0, message: Some("Something") })),
 	);
 	assert_eq!(
 		Module1_8::fail(system::Origin::<Runtime>::Root.into()),
-		Err(DispatchError::Module { index: 12, error: 0, message: Some("Something") }),
+		Err(DispatchError::Module(ModuleError { index: 12, error: 0, message: Some("Something") })),
 	);
 	assert_eq!(
 		Module1_9::fail(system::Origin::<Runtime>::Root.into()),
-		Err(DispatchError::Module { index: 13, error: 0, message: Some("Something") }),
+		Err(DispatchError::Module(ModuleError { index: 13, error: 0, message: Some("Something") })),
 	);
 }
 
diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs
index cf88255608..451cb2e7b8 100644
--- a/frame/support/test/tests/pallet.rs
+++ b/frame/support/test/tests/pallet.rs
@@ -29,7 +29,7 @@ use sp_io::{
 	hashing::{blake2_128, twox_128, twox_64},
 	TestExternalities,
 };
-use sp_runtime::DispatchError;
+use sp_runtime::{DispatchError, ModuleError};
 
 pub struct SomeType1;
 impl From<SomeType1> for u64 {
@@ -654,7 +654,11 @@ fn error_expand() {
 	);
 	assert_eq!(
 		DispatchError::from(pallet::Error::<Runtime>::InsufficientProposersBalance),
-		DispatchError::Module { index: 1, error: 0, message: Some("InsufficientProposersBalance") },
+		DispatchError::Module(ModuleError {
+			index: 1,
+			error: 0,
+			message: Some("InsufficientProposersBalance")
+		}),
 	);
 }
 
diff --git a/frame/support/test/tests/pallet_instance.rs b/frame/support/test/tests/pallet_instance.rs
index 4a8636919b..30b9bcda88 100644
--- a/frame/support/test/tests/pallet_instance.rs
+++ b/frame/support/test/tests/pallet_instance.rs
@@ -25,7 +25,7 @@ use sp_io::{
 	hashing::{blake2_128, twox_128, twox_64},
 	TestExternalities,
 };
-use sp_runtime::DispatchError;
+use sp_runtime::{DispatchError, ModuleError};
 
 #[frame_support::pallet]
 pub mod pallet {
@@ -341,7 +341,11 @@ fn error_expand() {
 	);
 	assert_eq!(
 		DispatchError::from(pallet::Error::<Runtime>::InsufficientProposersBalance),
-		DispatchError::Module { index: 1, error: 0, message: Some("InsufficientProposersBalance") },
+		DispatchError::Module(ModuleError {
+			index: 1,
+			error: 0,
+			message: Some("InsufficientProposersBalance")
+		}),
 	);
 
 	assert_eq!(
@@ -358,7 +362,11 @@ fn error_expand() {
 		DispatchError::from(
 			pallet::Error::<Runtime, pallet::Instance1>::InsufficientProposersBalance
 		),
-		DispatchError::Module { index: 2, error: 0, message: Some("InsufficientProposersBalance") },
+		DispatchError::Module(ModuleError {
+			index: 2,
+			error: 0,
+			message: Some("InsufficientProposersBalance")
+		}),
 	);
 }
 
diff --git a/test-utils/client/Cargo.toml b/test-utils/client/Cargo.toml
index 5f687b2489..640623aba6 100644
--- a/test-utils/client/Cargo.toml
+++ b/test-utils/client/Cargo.toml
@@ -31,7 +31,7 @@ sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" }
 sp-consensus = { version = "0.10.0-dev", path = "../../primitives/consensus/common" }
 sp-core = { version = "5.0.0", path = "../../primitives/core" }
 sp-keystore = { version = "0.11.0", path = "../../primitives/keystore" }
-sp-keyring = { version = "4.1.0-dev", path = "../../primitives/keyring" }
+sp-keyring = { version = "5.0.0", path = "../../primitives/keyring" }
 sp-runtime = { version = "5.0.0", path = "../../primitives/runtime" }
 sp-state-machine = { version = "0.11.0", path = "../../primitives/state-machine" }
 async-trait = "0.1.50"
diff --git a/test-utils/runtime/Cargo.toml b/test-utils/runtime/Cargo.toml
index f1b06d3b3e..2a9fcaf324 100644
--- a/test-utils/runtime/Cargo.toml
+++ b/test-utils/runtime/Cargo.toml
@@ -20,7 +20,7 @@ sp-block-builder = { version = "4.0.0-dev", default-features = false, path = "..
 codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
 scale-info = { version = "1.0", default-features = false, features = ["derive"] }
 sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../../primitives/inherents" }
-sp-keyring = { version = "4.1.0-dev", optional = true, path = "../../primitives/keyring" }
+sp-keyring = { version = "5.0.0", optional = true, path = "../../primitives/keyring" }
 memory-db = { version = "0.27.0", default-features = false }
 sp-offchain = { version = "4.0.0-dev", default-features = false, path = "../../primitives/offchain" }
 sp-core = { version = "5.0.0", default-features = false, path = "../../primitives/core" }