diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs
index cdee61af3f500baa284c8e1774a32dbba676af77..ace24f186d4c2b8d5fde304e43761fa1e66d3035 100644
--- a/substrate/bin/node/cli/src/service.rs
+++ b/substrate/bin/node/cli/src/service.rs
@@ -21,6 +21,7 @@
 //! Service implementation. Specialized wrapper over substrate service.
 
 use codec::Encode;
+use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE;
 use frame_system_rpc_runtime_api::AccountNonceApi;
 use futures::prelude::*;
 use kitchensink_runtime::RuntimeApi;
@@ -320,7 +321,11 @@ pub fn new_full_base(
 	let hwbench = if !disable_hardware_benchmarks {
 		config.database.path().map(|database_path| {
 			let _ = std::fs::create_dir_all(&database_path);
-			sc_sysinfo::gather_hwbench(Some(database_path))
+			sc_sysinfo::gather_hwbench(
+				Some(database_path),
+				SUBSTRATE_REFERENCE_HARDWARE.clone(),
+				config.role.is_authority(),
+			)
 		})
 	} else {
 		None
diff --git a/substrate/client/sysinfo/src/lib.rs b/substrate/client/sysinfo/src/lib.rs
index cef5a4d210df1bfaef4d02e5481b9f855c66e71d..f623bdae53e6686502a0064ae8f2a1f02a47d254 100644
--- a/substrate/client/sysinfo/src/lib.rs
+++ b/substrate/client/sysinfo/src/lib.rs
@@ -29,7 +29,8 @@ mod sysinfo_linux;
 pub use sysinfo::{
 	benchmark_cpu, benchmark_disk_random_writes, benchmark_disk_sequential_writes,
 	benchmark_memory, benchmark_sr25519_verify, gather_hwbench, gather_sysinfo,
-	serialize_throughput, serialize_throughput_option, Throughput,
+	serialize_throughput, serialize_throughput_option, Metric, Requirement, Requirements,
+	Throughput,
 };
 
 /// The operating system part of the current target triplet.
diff --git a/substrate/client/sysinfo/src/sysinfo.rs b/substrate/client/sysinfo/src/sysinfo.rs
index c66a6f6a62aed6091d8905e4d33cec5c8f61e806..800df4b8a41a9b4f539ed507624882297f68b38a 100644
--- a/substrate/client/sysinfo/src/sysinfo.rs
+++ b/substrate/client/sysinfo/src/sysinfo.rs
@@ -21,10 +21,10 @@ use crate::{ExecutionLimit, HwBench};
 use sc_telemetry::SysInfo;
 use sp_core::{sr25519, Pair};
 use sp_io::crypto::sr25519_verify;
-use sp_std::{fmt, prelude::*};
+use sp_std::{fmt, fmt::Formatter, prelude::*};
 
 use rand::{seq::SliceRandom, Rng, RngCore};
-use serde::Serializer;
+use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
 use std::{
 	fs::File,
 	io::{Seek, SeekFrom, Write},
@@ -33,6 +33,43 @@ use std::{
 	time::{Duration, Instant},
 };
 
+/// A single hardware metric.
+#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
+pub enum Metric {
+	/// SR25519 signature verification.
+	Sr25519Verify,
+	/// Blake2-256 hashing algorithm.
+	Blake2256,
+	/// Copying data in RAM.
+	MemCopy,
+	/// Disk sequential write.
+	DiskSeqWrite,
+	/// Disk random write.
+	DiskRndWrite,
+}
+
+impl Metric {
+	/// The category of the metric.
+	pub fn category(&self) -> &'static str {
+		match self {
+			Self::Sr25519Verify | Self::Blake2256 => "CPU",
+			Self::MemCopy => "Memory",
+			Self::DiskSeqWrite | Self::DiskRndWrite => "Disk",
+		}
+	}
+
+	/// The name of the metric. It is always prefixed by the [`self.category()`].
+	pub fn name(&self) -> &'static str {
+		match self {
+			Self::Sr25519Verify => "SR25519-Verify",
+			Self::Blake2256 => "BLAKE2-256",
+			Self::MemCopy => "Copy",
+			Self::DiskSeqWrite => "Seq Write",
+			Self::DiskRndWrite => "Rnd Write",
+		}
+	}
+}
+
 /// The unit in which the [`Throughput`] (bytes per second) is denoted.
 pub enum Unit {
 	GiBs,
@@ -137,6 +174,54 @@ where
 	serializer.serialize_none()
 }
 
+/// Serializes throughput into MiBs and represents it as `f64`.
+fn serialize_throughput_as_f64<S>(throughput: &Throughput, serializer: S) -> Result<S::Ok, S::Error>
+where
+	S: Serializer,
+{
+	serializer.serialize_f64(throughput.as_mibs())
+}
+
+struct ThroughputVisitor;
+impl<'de> Visitor<'de> for ThroughputVisitor {
+	type Value = Throughput;
+
+	fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
+		formatter.write_str("A value that is a f64.")
+	}
+
+	fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
+	where
+		E: serde::de::Error,
+	{
+		Ok(Throughput::from_mibs(value))
+	}
+}
+
+fn deserialize_throughput<'de, D>(deserializer: D) -> Result<Throughput, D::Error>
+where
+	D: Deserializer<'de>,
+{
+	Ok(deserializer.deserialize_f64(ThroughputVisitor))?
+}
+
+/// Multiple requirements for the hardware.
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
+pub struct Requirements(pub Vec<Requirement>);
+
+/// A single requirement for the hardware.
+#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
+pub struct Requirement {
+	/// The metric to measure.
+	pub metric: Metric,
+	/// The minimal throughput that needs to be archived for this requirement.
+	#[serde(
+		serialize_with = "serialize_throughput_as_f64",
+		deserialize_with = "deserialize_throughput"
+	)]
+	pub minimum: Throughput,
+}
+
 #[inline(always)]
 pub(crate) fn benchmark<E>(
 	name: &str,
@@ -503,8 +588,14 @@ pub fn benchmark_sr25519_verify(limit: ExecutionLimit) -> Throughput {
 
 /// Benchmarks the hardware and returns the results of those benchmarks.
 ///
-/// Optionally accepts a path to a `scratch_directory` to use to benchmark the disk.
-pub fn gather_hwbench(scratch_directory: Option<&Path>) -> HwBench {
+/// Optionally accepts a path to a `scratch_directory` to use to benchmark the
+/// disk. Also accepts the `requirements` for the hardware benchmark and a
+/// boolean to specify if the node is an authority.
+pub fn gather_hwbench(
+	scratch_directory: Option<&Path>,
+	requirements: Requirements,
+	is_authority: bool,
+) -> HwBench {
 	#[allow(unused_mut)]
 	let mut hwbench = HwBench {
 		cpu_hashrate_score: benchmark_cpu(DEFAULT_CPU_EXECUTION_LIMIT),
@@ -534,9 +625,45 @@ pub fn gather_hwbench(scratch_directory: Option<&Path>) -> HwBench {
 			};
 	}
 
+	if is_authority {
+		ensure_requirements(hwbench.clone(), requirements);
+	}
+
 	hwbench
 }
 
+fn ensure_requirements(hwbench: HwBench, requirements: Requirements) {
+	let mut failed = 0;
+	for requirement in requirements.0.iter() {
+		match requirement.metric {
+			Metric::Blake2256 =>
+				if requirement.minimum > hwbench.cpu_hashrate_score {
+					failed += 1;
+				},
+			Metric::MemCopy =>
+				if requirement.minimum > hwbench.memory_memcpy_score {
+					failed += 1;
+				},
+			Metric::DiskSeqWrite =>
+				if let Some(score) = hwbench.disk_sequential_write_score {
+					if requirement.minimum > score {
+						failed += 1;
+					}
+				},
+			Metric::DiskRndWrite =>
+				if let Some(score) = hwbench.disk_random_write_score {
+					if requirement.minimum > score {
+						failed += 1;
+					}
+				},
+			Metric::Sr25519Verify => {},
+		}
+	}
+	if failed != 0 {
+		log::warn!("⚠️  Your hardware performance score was less than expected for role 'Authority'. See https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware");
+	}
+}
+
 #[cfg(test)]
 mod tests {
 	use super::*;
diff --git a/substrate/utils/frame/benchmarking-cli/src/lib.rs b/substrate/utils/frame/benchmarking-cli/src/lib.rs
index a44a208b16ae9763dcf1532b30185eff4bb41570..5723a8038fdbb9f559db71dbd49f2cc9c3834db2 100644
--- a/substrate/utils/frame/benchmarking-cli/src/lib.rs
+++ b/substrate/utils/frame/benchmarking-cli/src/lib.rs
@@ -27,7 +27,7 @@ mod storage;
 
 pub use block::BlockCmd;
 pub use extrinsic::{ExtrinsicBuilder, ExtrinsicCmd, ExtrinsicFactory};
-pub use machine::{MachineCmd, Requirements, SUBSTRATE_REFERENCE_HARDWARE};
+pub use machine::{MachineCmd, SUBSTRATE_REFERENCE_HARDWARE};
 pub use overhead::OverheadCmd;
 pub use pallet::PalletCmd;
 pub use sc_service::BasePath;
diff --git a/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs b/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs
index 50c88ec74646c36d420b5a4c78638d0bc71a0e23..318c193d7c08c0226567416d707846b175084b20 100644
--- a/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs
+++ b/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs
@@ -18,40 +18,7 @@
 //! Contains types to define hardware requirements.
 
 use lazy_static::lazy_static;
-use sc_sysinfo::Throughput;
-use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
-use sp_std::{fmt, fmt::Formatter};
-
-/// Serializes throughput into MiBs and represents it as `f64`.
-fn serialize_throughput_as_f64<S>(throughput: &Throughput, serializer: S) -> Result<S::Ok, S::Error>
-where
-	S: Serializer,
-{
-	serializer.serialize_f64(throughput.as_mibs())
-}
-
-struct ThroughputVisitor;
-impl<'de> Visitor<'de> for ThroughputVisitor {
-	type Value = Throughput;
-
-	fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
-		formatter.write_str("A value that is a f64.")
-	}
-
-	fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
-	where
-		E: serde::de::Error,
-	{
-		Ok(Throughput::from_mibs(value))
-	}
-}
-
-fn deserialize_throughput<'de, D>(deserializer: D) -> Result<Throughput, D::Error>
-where
-	D: Deserializer<'de>,
-{
-	Ok(deserializer.deserialize_f64(ThroughputVisitor))?
-}
+use sc_sysinfo::Requirements;
 
 lazy_static! {
 	/// The hardware requirements as measured on reference hardware.
@@ -67,62 +34,6 @@ lazy_static! {
 	};
 }
 
-/// Multiple requirements for the hardware.
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
-pub struct Requirements(pub Vec<Requirement>);
-
-/// A single requirement for the hardware.
-#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
-pub struct Requirement {
-	/// The metric to measure.
-	pub metric: Metric,
-	/// The minimal throughput that needs to be archived for this requirement.
-	#[serde(
-		serialize_with = "serialize_throughput_as_f64",
-		deserialize_with = "deserialize_throughput"
-	)]
-	pub minimum: Throughput,
-}
-
-/// A single hardware metric.
-///
-/// The implementation of these is in `sc-sysinfo`.
-#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
-pub enum Metric {
-	/// SR25519 signature verification.
-	Sr25519Verify,
-	/// Blake2-256 hashing algorithm.
-	Blake2256,
-	/// Copying data in RAM.
-	MemCopy,
-	/// Disk sequential write.
-	DiskSeqWrite,
-	/// Disk random write.
-	DiskRndWrite,
-}
-
-impl Metric {
-	/// The category of the metric.
-	pub fn category(&self) -> &'static str {
-		match self {
-			Self::Sr25519Verify | Self::Blake2256 => "CPU",
-			Self::MemCopy => "Memory",
-			Self::DiskSeqWrite | Self::DiskRndWrite => "Disk",
-		}
-	}
-
-	/// The name of the metric. It is always prefixed by the [`self::category()`].
-	pub fn name(&self) -> &'static str {
-		match self {
-			Self::Sr25519Verify => "SR25519-Verify",
-			Self::Blake2256 => "BLAKE2-256",
-			Self::MemCopy => "Copy",
-			Self::DiskSeqWrite => "Seq Write",
-			Self::DiskRndWrite => "Rnd Write",
-		}
-	}
-}
-
 #[cfg(test)]
 mod tests {
 	use super::*;
diff --git a/substrate/utils/frame/benchmarking-cli/src/machine/mod.rs b/substrate/utils/frame/benchmarking-cli/src/machine/mod.rs
index 82b4e5be7358e7c0e3c4899d33b7e0558ae36e5a..bcffef255e5b510690ac9b1e8b550239bb81b7a2 100644
--- a/substrate/utils/frame/benchmarking-cli/src/machine/mod.rs
+++ b/substrate/utils/frame/benchmarking-cli/src/machine/mod.rs
@@ -30,11 +30,12 @@ use sc_cli::{CliConfiguration, Result, SharedParams};
 use sc_service::Configuration;
 use sc_sysinfo::{
 	benchmark_cpu, benchmark_disk_random_writes, benchmark_disk_sequential_writes,
-	benchmark_memory, benchmark_sr25519_verify, ExecutionLimit, Throughput,
+	benchmark_memory, benchmark_sr25519_verify, ExecutionLimit, Metric, Requirement, Requirements,
+	Throughput,
 };
 
 use crate::shared::check_build_profile;
-pub use hardware::{Metric, Requirement, Requirements, SUBSTRATE_REFERENCE_HARDWARE};
+pub use hardware::SUBSTRATE_REFERENCE_HARDWARE;
 
 /// Command to benchmark the hardware.
 ///