From 72bde2889f2b2635601378b13bddda4ca65c35c5 Mon Sep 17 00:00:00 2001
From: Sebastian Kunert <skunert49@gmail.com>
Date: Wed, 20 Jul 2022 12:23:25 +0200
Subject: [PATCH] Introduce async runtime calling trait for runtime-api
 subsystem (#5782)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Implement OverseerRuntimeClient

* blockchainevents

* Update patches

* Finish merging rntime-api subsystem

* First version that is able to produce blocks

* Make OverseerRuntimeClient async

* Move overseer notification stream forwarding to cumulus

* Remove unused imports

* Add more logging to collator-protocol

* Lockfile

* Use hashes in OverseerRuntimeClient

* Move OverseerRuntimeClient into extra module

* Fix old session info call and make HeadSupportsParachain async

* Improve naming of trait

* Cleanup

* Remove unused From trait implementation

* Remove unwanted debug print

* Move trait to polkadot-node-subsystem-types

* Add sections to runtime client

Co-authored-by: Davide Galassi <davxy@datawok.net>

* Reorder methods

* Fix spelling

* Fix spacing in Cargo.toml

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* Remove unused babe methods

Co-authored-by: Davide Galassi <davxy@datawok.net>
Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
---
 polkadot/Cargo.lock                           |  11 +-
 polkadot/node/core/approval-voting/Cargo.toml |   1 +
 .../node/core/approval-voting/src/tests.rs    |   4 +-
 polkadot/node/core/runtime-api/Cargo.toml     |   7 +-
 polkadot/node/core/runtime-api/src/lib.rs     |  57 +--
 polkadot/node/core/runtime-api/src/tests.rs   |  18 +-
 polkadot/node/overseer/Cargo.toml             |   1 +
 .../node/overseer/examples/minimal-example.rs |   5 +-
 polkadot/node/overseer/src/lib.rs             |  37 +-
 polkadot/node/overseer/src/tests.rs           |   4 +-
 .../node/subsystem-test-helpers/src/lib.rs    |   4 +-
 polkadot/node/subsystem-types/Cargo.toml      |   4 +
 polkadot/node/subsystem-types/src/lib.rs      |   3 +
 .../subsystem-types/src/runtime_client.rs     | 384 ++++++++++++++++++
 polkadot/rustfmt.toml                         |   1 +
 15 files changed, 467 insertions(+), 74 deletions(-)
 create mode 100644 polkadot/node/subsystem-types/src/runtime_client.rs

diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock
index 50e76501639..4a2da99419d 100644
--- a/polkadot/Cargo.lock
+++ b/polkadot/Cargo.lock
@@ -327,9 +327,9 @@ checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0"
 
 [[package]]
 name = "async-trait"
-version = "0.1.53"
+version = "0.1.56"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600"
+checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -6295,6 +6295,7 @@ name = "polkadot-node-core-approval-voting"
 version = "0.9.26"
 dependencies = [
  "assert_matches",
+ "async-trait",
  "bitvec",
  "derive_more",
  "futures",
@@ -6590,6 +6591,7 @@ dependencies = [
  "polkadot-node-primitives",
  "polkadot-node-subsystem",
  "polkadot-node-subsystem-test-helpers",
+ "polkadot-node-subsystem-types",
  "polkadot-node-subsystem-util",
  "polkadot-primitives",
  "polkadot-primitives-test-helpers",
@@ -6720,6 +6722,7 @@ dependencies = [
 name = "polkadot-node-subsystem-types"
 version = "0.9.26"
 dependencies = [
+ "async-trait",
  "derive_more",
  "futures",
  "orchestra",
@@ -6730,6 +6733,9 @@ dependencies = [
  "polkadot-statement-table",
  "sc-network",
  "smallvec",
+ "sp-api",
+ "sp-authority-discovery",
+ "sp-consensus-babe",
  "substrate-prometheus-endpoint",
  "thiserror",
 ]
@@ -6779,6 +6785,7 @@ name = "polkadot-overseer"
 version = "0.9.26"
 dependencies = [
  "assert_matches",
+ "async-trait",
  "femme",
  "futures",
  "futures-timer",
diff --git a/polkadot/node/core/approval-voting/Cargo.toml b/polkadot/node/core/approval-voting/Cargo.toml
index 646817a5aa4..79f78bba7f6 100644
--- a/polkadot/node/core/approval-voting/Cargo.toml
+++ b/polkadot/node/core/approval-voting/Cargo.toml
@@ -31,6 +31,7 @@ sp-application-crypto = { git = "https://github.com/paritytech/substrate", branc
 sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
 
 [dev-dependencies]
+async-trait = "0.1.56"
 parking_lot = "0.12.0"
 rand_core = "0.5.1" #                                                                       should match schnorrkel
 sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" }
diff --git a/polkadot/node/core/approval-voting/src/tests.rs b/polkadot/node/core/approval-voting/src/tests.rs
index 25dcfcdb4e8..641e6cb3a4f 100644
--- a/polkadot/node/core/approval-voting/src/tests.rs
+++ b/polkadot/node/core/approval-voting/src/tests.rs
@@ -38,6 +38,7 @@ use polkadot_primitives::v2::{
 use std::time::Duration;
 
 use assert_matches::assert_matches;
+use async_trait::async_trait;
 use parking_lot::Mutex;
 use sp_keyring::sr25519::Keyring as Sr25519Keyring;
 use sp_keystore::CryptoStore;
@@ -117,8 +118,9 @@ pub mod test_constants {
 
 struct MockSupportsParachains;
 
+#[async_trait]
 impl HeadSupportsParachains for MockSupportsParachains {
-	fn head_supports_parachains(&self, _head: &Hash) -> bool {
+	async fn head_supports_parachains(&self, _head: &Hash) -> bool {
 		true
 	}
 }
diff --git a/polkadot/node/core/runtime-api/Cargo.toml b/polkadot/node/core/runtime-api/Cargo.toml
index 3af58ab5d2a..bf14836b9b1 100644
--- a/polkadot/node/core/runtime-api/Cargo.toml
+++ b/polkadot/node/core/runtime-api/Cargo.toml
@@ -10,15 +10,16 @@ gum = { package = "tracing-gum", path = "../../gum" }
 memory-lru = "0.1.0"
 parity-util-mem = { version = "0.11.0", default-features = false }
 
-sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
-sp-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master" }
 sp-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = "master" }
 
 polkadot-primitives = { path = "../../../primitives" }
-polkadot-node-subsystem = {path = "../../subsystem" }
+polkadot-node-subsystem = { path = "../../subsystem" }
+polkadot-node-subsystem-types = { path = "../../subsystem-types" }
 polkadot-node-subsystem-util = { path = "../../subsystem-util" }
 
 [dev-dependencies]
+sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sp-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master" }
 sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
 sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" }
 futures = { version = "0.3.21", features = ["thread-pool"] }
diff --git a/polkadot/node/core/runtime-api/src/lib.rs b/polkadot/node/core/runtime-api/src/lib.rs
index 691ca9e655d..a815b76a8d7 100644
--- a/polkadot/node/core/runtime-api/src/lib.rs
+++ b/polkadot/node/core/runtime-api/src/lib.rs
@@ -27,14 +27,8 @@ use polkadot_node_subsystem::{
 	messages::{RuntimeApiMessage, RuntimeApiRequest as Request},
 	overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, SubsystemResult,
 };
-use polkadot_primitives::{
-	runtime_api::ParachainHost,
-	v2::{Block, BlockId, Hash},
-};
-
-use sp_api::ProvideRuntimeApi;
-use sp_authority_discovery::AuthorityDiscoveryApi;
-use sp_consensus_babe::BabeApi;
+use polkadot_node_subsystem_types::RuntimeApiSubsystemClient;
+use polkadot_primitives::v2::Hash;
 
 use cache::{RequestResult, RequestResultCache};
 use futures::{channel::oneshot, prelude::*, select, stream::FuturesUnordered};
@@ -88,8 +82,7 @@ impl<Client> RuntimeApiSubsystem<Client> {
 #[overseer::subsystem(RuntimeApi, error = SubsystemError, prefix = self::overseer)]
 impl<Client, Context> RuntimeApiSubsystem<Client>
 where
-	Client: ProvideRuntimeApi<Block> + Send + 'static + Sync,
-	Client::Api: ParachainHost<Block> + BabeApi<Block> + AuthorityDiscoveryApi<Block>,
+	Client: RuntimeApiSubsystemClient + Send + Sync + 'static,
 {
 	fn start(self, ctx: Context) -> SpawnedSubsystem {
 		SpawnedSubsystem { future: run(ctx, self).boxed(), name: "runtime-api-subsystem" }
@@ -98,8 +91,7 @@ where
 
 impl<Client> RuntimeApiSubsystem<Client>
 where
-	Client: ProvideRuntimeApi<Block> + Send + 'static + Sync,
-	Client::Api: ParachainHost<Block> + BabeApi<Block> + AuthorityDiscoveryApi<Block>,
+	Client: RuntimeApiSubsystemClient + Send + 'static + Sync,
 {
 	fn store_cache(&mut self, result: RequestResult) {
 		use RequestResult::*;
@@ -282,7 +274,7 @@ where
 		};
 
 		let request = async move {
-			let result = make_runtime_api_request(client, metrics, relay_parent, request);
+			let result = make_runtime_api_request(client, metrics, relay_parent, request).await;
 			let _ = sender.send(result);
 		}
 		.boxed();
@@ -317,8 +309,7 @@ async fn run<Client, Context>(
 	mut subsystem: RuntimeApiSubsystem<Client>,
 ) -> SubsystemResult<()>
 where
-	Client: ProvideRuntimeApi<Block> + Send + Sync + 'static,
-	Client::Api: ParachainHost<Block> + BabeApi<Block> + AuthorityDiscoveryApi<Block>,
+	Client: RuntimeApiSubsystemClient + Send + Sync + 'static,
 {
 	loop {
 		// Let's add some back pressure when the subsystem is running at `MAX_PARALLEL_REQUESTS`.
@@ -348,26 +339,21 @@ where
 	}
 }
 
-fn make_runtime_api_request<Client>(
+async fn make_runtime_api_request<Client>(
 	client: Arc<Client>,
 	metrics: Metrics,
 	relay_parent: Hash,
 	request: Request,
 ) -> Option<RequestResult>
 where
-	Client: ProvideRuntimeApi<Block>,
-	Client::Api: ParachainHost<Block> + BabeApi<Block> + AuthorityDiscoveryApi<Block>,
+	Client: RuntimeApiSubsystemClient + 'static,
 {
-	use sp_api::ApiExt;
-
 	let _timer = metrics.time_make_runtime_api_request();
 
 	macro_rules! query {
 		($req_variant:ident, $api_name:ident ($($param:expr),*), ver = $version:literal, $sender:expr) => {{
 			let sender = $sender;
-			let api = client.runtime_api();
-
-			let runtime_version = api.api_version::<dyn ParachainHost<Block>>(&BlockId::Hash(relay_parent))
+			let runtime_version = client.api_version_parachain_host(relay_parent).await
 				.unwrap_or_else(|e| {
 					gum::warn!(
 						target: LOG_TARGET,
@@ -385,7 +371,7 @@ where
 				});
 
 			let res = if runtime_version >= $version {
-				api.$api_name(&BlockId::Hash(relay_parent) $(, $param.clone() )*)
+				client.$api_name(relay_parent $(, $param.clone() )*).await
 					.map_err(|e| RuntimeApiError::Execution {
 						runtime_api_name: stringify!($api_name),
 						source: std::sync::Arc::new(e),
@@ -404,11 +390,7 @@ where
 
 	match request {
 		Request::Version(sender) => {
-			let api = client.runtime_api();
-
-			let runtime_version = match api
-				.api_version::<dyn ParachainHost<Block>>(&BlockId::Hash(relay_parent))
-			{
+			let runtime_version = match client.api_version_parachain_host(relay_parent).await {
 				Ok(Some(v)) => Ok(v),
 				Ok(None) => Err(RuntimeApiError::NotSupported { runtime_api_name: "api_version" }),
 				Err(e) => Err(RuntimeApiError::Execution {
@@ -465,25 +447,24 @@ where
 		Request::CandidateEvents(sender) =>
 			query!(CandidateEvents, candidate_events(), ver = 1, sender),
 		Request::SessionInfo(index, sender) => {
-			let api = client.runtime_api();
-			let block_id = BlockId::Hash(relay_parent);
-
-			let api_version = api
-				.api_version::<dyn ParachainHost<Block>>(&BlockId::Hash(relay_parent))
+			let api_version = client
+				.api_version_parachain_host(relay_parent)
+				.await
 				.unwrap_or_default()
 				.unwrap_or_default();
 
 			let res = if api_version >= 2 {
-				let res =
-					api.session_info(&block_id, index).map_err(|e| RuntimeApiError::Execution {
+				let res = client.session_info(relay_parent, index).await.map_err(|e| {
+					RuntimeApiError::Execution {
 						runtime_api_name: "SessionInfo",
 						source: std::sync::Arc::new(e),
-					});
+					}
+				});
 				metrics.on_request(res.is_ok());
 				res
 			} else {
 				#[allow(deprecated)]
-				let res = api.session_info_before_version_2(&block_id, index).map_err(|e| {
+				let res = client.session_info_before_version_2(relay_parent, index).await.map_err(|e| {
 					RuntimeApiError::Execution {
 						runtime_api_name: "SessionInfo",
 						source: std::sync::Arc::new(e),
diff --git a/polkadot/node/core/runtime-api/src/tests.rs b/polkadot/node/core/runtime-api/src/tests.rs
index fdcd66ecf2a..b1a1bba7376 100644
--- a/polkadot/node/core/runtime-api/src/tests.rs
+++ b/polkadot/node/core/runtime-api/src/tests.rs
@@ -20,13 +20,19 @@ use ::test_helpers::{dummy_committed_candidate_receipt, dummy_validation_code};
 use polkadot_node_primitives::{BabeAllowedSlots, BabeEpoch, BabeEpochConfiguration};
 use polkadot_node_subsystem::SpawnGlue;
 use polkadot_node_subsystem_test_helpers::make_subsystem_context;
-use polkadot_primitives::v2::{
-	AuthorityDiscoveryId, BlockNumber, CandidateEvent, CandidateHash, CommittedCandidateReceipt,
-	CoreState, DisputeState, GroupRotationInfo, Id as ParaId, InboundDownwardMessage,
-	InboundHrmpMessage, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement,
-	ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash,
-	ValidatorId, ValidatorIndex, ValidatorSignature,
+use polkadot_primitives::{
+	runtime_api::ParachainHost,
+	v2::{
+		AuthorityDiscoveryId, Block, BlockNumber, CandidateEvent, CandidateHash,
+		CommittedCandidateReceipt, CoreState, DisputeState, GroupRotationInfo, Id as ParaId,
+		InboundDownwardMessage, InboundHrmpMessage, OccupiedCoreAssumption,
+		PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo,
+		ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature,
+	},
 };
+use sp_api::ProvideRuntimeApi;
+use sp_authority_discovery::AuthorityDiscoveryApi;
+use sp_consensus_babe::BabeApi;
 use sp_core::testing::TaskExecutor;
 use std::{
 	collections::{BTreeMap, HashMap},
diff --git a/polkadot/node/overseer/Cargo.toml b/polkadot/node/overseer/Cargo.toml
index 8341d14f925..c586f748f95 100644
--- a/polkadot/node/overseer/Cargo.toml
+++ b/polkadot/node/overseer/Cargo.toml
@@ -20,6 +20,7 @@ gum = { package = "tracing-gum", path = "../gum" }
 lru = "0.7"
 parity-util-mem = { version = "0.11.0", default-features = false }
 sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
+async-trait = "0.1.56"
 
 [dev-dependencies]
 metered = { package = "prioritized-metered-channel", path = "../metered-channel" }
diff --git a/polkadot/node/overseer/examples/minimal-example.rs b/polkadot/node/overseer/examples/minimal-example.rs
index 6033815ddd5..be6779819eb 100644
--- a/polkadot/node/overseer/examples/minimal-example.rs
+++ b/polkadot/node/overseer/examples/minimal-example.rs
@@ -20,6 +20,7 @@
 
 use futures::{channel::oneshot, pending, pin_mut, select, stream, FutureExt, StreamExt};
 use futures_timer::Delay;
+use orchestra::async_trait;
 use std::time::Duration;
 
 use ::test_helpers::{dummy_candidate_descriptor, dummy_hash};
@@ -34,8 +35,10 @@ use polkadot_overseer::{
 use polkadot_primitives::v2::{CandidateReceipt, Hash};
 
 struct AlwaysSupportsParachains;
+
+#[async_trait]
 impl HeadSupportsParachains for AlwaysSupportsParachains {
-	fn head_supports_parachains(&self, _head: &Hash) -> bool {
+	async fn head_supports_parachains(&self, _head: &Hash) -> bool {
 		true
 	}
 }
diff --git a/polkadot/node/overseer/src/lib.rs b/polkadot/node/overseer/src/lib.rs
index fefcc4dba0c..43386352b77 100644
--- a/polkadot/node/overseer/src/lib.rs
+++ b/polkadot/node/overseer/src/lib.rs
@@ -71,11 +71,7 @@ use futures::{channel::oneshot, future::BoxFuture, select, Future, FutureExt, St
 use lru::LruCache;
 
 use client::{BlockImportNotification, BlockchainEvents, FinalityNotification};
-use polkadot_primitives::{
-	runtime_api::ParachainHost,
-	v2::{Block, BlockId, BlockNumber, Hash},
-};
-use sp_api::{ApiExt, ProvideRuntimeApi};
+use polkadot_primitives::v2::{Block, BlockNumber, Hash};
 
 use polkadot_node_subsystem_types::messages::{
 	ApprovalDistributionMessage, ApprovalVotingMessage, AvailabilityDistributionMessage,
@@ -89,6 +85,7 @@ use polkadot_node_subsystem_types::messages::{
 pub use polkadot_node_subsystem_types::{
 	errors::{SubsystemError, SubsystemResult},
 	jaeger, ActivatedLeaf, ActiveLeavesUpdate, LeafStatus, OverseerSignal,
+	RuntimeApiSubsystemClient,
 };
 
 pub mod metrics;
@@ -157,25 +154,20 @@ impl<S: SpawnNamed + Clone + Send + Sync> crate::gen::Spawner for SpawnGlue<S> {
 }
 
 /// Whether a header supports parachain consensus or not.
+#[async_trait::async_trait]
 pub trait HeadSupportsParachains {
 	/// Return true if the given header supports parachain consensus. Otherwise, false.
-	fn head_supports_parachains(&self, head: &Hash) -> bool;
+	async fn head_supports_parachains(&self, head: &Hash) -> bool;
 }
 
+#[async_trait::async_trait]
 impl<Client> HeadSupportsParachains for Arc<Client>
 where
-	Client: ProvideRuntimeApi<Block>,
-	Client::Api: ParachainHost<Block>,
+	Client: RuntimeApiSubsystemClient + Sync + Send,
 {
-	fn head_supports_parachains(&self, head: &Hash) -> bool {
-		let id = BlockId::Hash(*head);
+	async fn head_supports_parachains(&self, head: &Hash) -> bool {
 		// Check that the `ParachainHost` runtime api is at least with version 1 present on chain.
-		self.runtime_api()
-			.api_version::<dyn ParachainHost<Block>>(&id)
-			.ok()
-			.flatten()
-			.unwrap_or(0) >=
-			1
+		self.api_version_parachain_host(*head).await.ok().flatten().unwrap_or(0) >= 1
 	}
 }
 
@@ -421,9 +413,12 @@ pub async fn forward_events<P: BlockchainEvents<Block>>(client: Arc<P>, mut hand
 /// # fn main() { executor::block_on(async move {
 ///
 /// struct AlwaysSupportsParachains;
+///
+/// #[async_trait::async_trait]
 /// impl HeadSupportsParachains for AlwaysSupportsParachains {
-///      fn head_supports_parachains(&self, _head: &Hash) -> bool { true }
+///      async fn head_supports_parachains(&self, _head: &Hash) -> bool { true }
 /// }
+///
 /// let spawner = sp_core::testing::TaskExecutor::new();
 /// let (overseer, _handle) = dummy_overseer_builder(spawner, AlwaysSupportsParachains, None)
 ///		.unwrap()
@@ -718,7 +713,7 @@ where
 		// Notify about active leaves on startup before starting the loop
 		for (hash, number) in std::mem::take(&mut self.leaves) {
 			let _ = self.active_leaves.insert(hash, number);
-			if let Some((span, status)) = self.on_head_activated(&hash, None) {
+			if let Some((span, status)) = self.on_head_activated(&hash, None).await {
 				let update =
 					ActiveLeavesUpdate::start_work(ActivatedLeaf { hash, number, status, span });
 				self.broadcast_signal(OverseerSignal::ActiveLeaves(update)).await?;
@@ -780,7 +775,7 @@ where
 			},
 		};
 
-		let mut update = match self.on_head_activated(&block.hash, Some(block.parent_hash)) {
+		let mut update = match self.on_head_activated(&block.hash, Some(block.parent_hash)).await {
 			Some((span, status)) => ActiveLeavesUpdate::start_work(ActivatedLeaf {
 				hash: block.hash,
 				number: block.number,
@@ -837,12 +832,12 @@ where
 
 	/// Handles a header activation. If the header's state doesn't support the parachains API,
 	/// this returns `None`.
-	fn on_head_activated(
+	async fn on_head_activated(
 		&mut self,
 		hash: &Hash,
 		parent_hash: Option<Hash>,
 	) -> Option<(Arc<jaeger::Span>, LeafStatus)> {
-		if !self.supports_parachains.head_supports_parachains(hash) {
+		if !self.supports_parachains.head_supports_parachains(hash).await {
 			return None
 		}
 
diff --git a/polkadot/node/overseer/src/tests.rs b/polkadot/node/overseer/src/tests.rs
index 25a6d07e393..121c707c254 100644
--- a/polkadot/node/overseer/src/tests.rs
+++ b/polkadot/node/overseer/src/tests.rs
@@ -14,6 +14,7 @@
 // You should have received a copy of the GNU General Public License
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
+use async_trait::async_trait;
 use futures::{executor, pending, pin_mut, poll, select, stream, FutureExt};
 use std::{collections::HashMap, sync::atomic, task::Poll};
 
@@ -154,8 +155,9 @@ where
 
 struct MockSupportsParachains;
 
+#[async_trait]
 impl HeadSupportsParachains for MockSupportsParachains {
-	fn head_supports_parachains(&self, _head: &Hash) -> bool {
+	async fn head_supports_parachains(&self, _head: &Hash) -> bool {
 		true
 	}
 }
diff --git a/polkadot/node/subsystem-test-helpers/src/lib.rs b/polkadot/node/subsystem-test-helpers/src/lib.rs
index cc263266366..e2e61c2006d 100644
--- a/polkadot/node/subsystem-test-helpers/src/lib.rs
+++ b/polkadot/node/subsystem-test-helpers/src/lib.rs
@@ -401,8 +401,10 @@ mod tests {
 	use sp_core::traits::SpawnNamed;
 
 	struct AlwaysSupportsParachains;
+
+	#[async_trait::async_trait]
 	impl HeadSupportsParachains for AlwaysSupportsParachains {
-		fn head_supports_parachains(&self, _head: &Hash) -> bool {
+		async fn head_supports_parachains(&self, _head: &Hash) -> bool {
 			true
 		}
 	}
diff --git a/polkadot/node/subsystem-types/Cargo.toml b/polkadot/node/subsystem-types/Cargo.toml
index 2e17acf6ff7..9a646d81965 100644
--- a/polkadot/node/subsystem-types/Cargo.toml
+++ b/polkadot/node/subsystem-types/Cargo.toml
@@ -15,6 +15,10 @@ polkadot-statement-table = { path = "../../statement-table" }
 polkadot-node-jaeger = { path = "../jaeger" }
 orchestra = { path = "../orchestra" }
 sc-network = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sp-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = "master" }
+sp-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master" }
 smallvec = "1.8.0"
 substrate-prometheus-endpoint = { git = "https://github.com/paritytech/substrate", branch = "master" }
 thiserror = "1.0.31"
+async-trait = "0.1.56"
diff --git a/polkadot/node/subsystem-types/src/lib.rs b/polkadot/node/subsystem-types/src/lib.rs
index f6ff9bea737..b5a8c67fc58 100644
--- a/polkadot/node/subsystem-types/src/lib.rs
+++ b/polkadot/node/subsystem-types/src/lib.rs
@@ -30,6 +30,9 @@ use smallvec::SmallVec;
 pub mod errors;
 pub mod messages;
 
+mod runtime_client;
+pub use runtime_client::RuntimeApiSubsystemClient;
+
 pub use jaeger::*;
 pub use polkadot_node_jaeger as jaeger;
 
diff --git a/polkadot/node/subsystem-types/src/runtime_client.rs b/polkadot/node/subsystem-types/src/runtime_client.rs
new file mode 100644
index 00000000000..2aa9e2bffb8
--- /dev/null
+++ b/polkadot/node/subsystem-types/src/runtime_client.rs
@@ -0,0 +1,384 @@
+// Copyright 2022 Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot 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.
+
+// Polkadot 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 Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+use async_trait::async_trait;
+use polkadot_primitives::{
+	runtime_api::ParachainHost,
+	v2::{
+		Block, BlockId, BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash,
+		CommittedCandidateReceipt, CoreState, DisputeState, GroupRotationInfo, Hash, Id,
+		InboundDownwardMessage, InboundHrmpMessage, OccupiedCoreAssumption,
+		PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo,
+		ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature,
+	},
+};
+use sp_api::{ApiError, ApiExt, ProvideRuntimeApi};
+use sp_authority_discovery::AuthorityDiscoveryApi;
+use sp_consensus_babe::{BabeApi, Epoch};
+use std::collections::BTreeMap;
+
+/// Exposes all runtime calls that are used by the runtime API subsystem.
+#[async_trait]
+pub trait RuntimeApiSubsystemClient {
+	/// Parachain host API version
+	async fn api_version_parachain_host(&self, at: Hash) -> Result<Option<u32>, ApiError>;
+
+	// === ParachainHost API ===
+
+	/// Get the current validators.
+	async fn validators(&self, at: Hash) -> Result<Vec<ValidatorId>, ApiError>;
+
+	/// Returns the validator groups and rotation info localized based on the hypothetical child
+	///  of a block whose state  this is invoked on. Note that `now` in the `GroupRotationInfo`
+	/// should be the successor of the number of the block.
+	async fn validator_groups(
+		&self,
+		at: Hash,
+	) -> Result<(Vec<Vec<ValidatorIndex>>, GroupRotationInfo<BlockNumber>), ApiError>;
+
+	/// Yields information on all availability cores as relevant to the child block.
+	/// Cores are either free or occupied. Free cores can have paras assigned to them.
+	async fn availability_cores(
+		&self,
+		at: Hash,
+	) -> Result<Vec<CoreState<Hash, BlockNumber>>, ApiError>;
+
+	/// Yields the persisted validation data for the given `ParaId` along with an assumption that
+	/// should be used if the para currently occupies a core.
+	///
+	/// Returns `None` if either the para is not registered or the assumption is `Freed`
+	/// and the para already occupies a core.
+	async fn persisted_validation_data(
+		&self,
+		at: Hash,
+		para_id: Id,
+		assumption: OccupiedCoreAssumption,
+	) -> Result<Option<PersistedValidationData<Hash, BlockNumber>>, ApiError>;
+
+	/// Returns the persisted validation data for the given `ParaId` along with the corresponding
+	/// validation code hash. Instead of accepting assumption about the para, matches the validation
+	/// data hash against an expected one and yields `None` if they're not equal.
+	async fn assumed_validation_data(
+		&self,
+		at: Hash,
+		para_id: Id,
+		expected_persisted_validation_data_hash: Hash,
+	) -> Result<Option<(PersistedValidationData<Hash, BlockNumber>, ValidationCodeHash)>, ApiError>;
+
+	/// Checks if the given validation outputs pass the acceptance criteria.
+	async fn check_validation_outputs(
+		&self,
+		at: Hash,
+		para_id: Id,
+		outputs: CandidateCommitments,
+	) -> Result<bool, ApiError>;
+
+	/// Returns the session index expected at a child of the block.
+	///
+	/// This can be used to instantiate a `SigningContext`.
+	async fn session_index_for_child(&self, at: Hash) -> Result<SessionIndex, ApiError>;
+
+	/// Fetch the validation code used by a para, making the given `OccupiedCoreAssumption`.
+	///
+	/// Returns `None` if either the para is not registered or the assumption is `Freed`
+	/// and the para already occupies a core.
+	async fn validation_code(
+		&self,
+		at: Hash,
+		para_id: Id,
+		assumption: OccupiedCoreAssumption,
+	) -> Result<Option<ValidationCode>, ApiError>;
+
+	/// Get the receipt of a candidate pending availability. This returns `Some` for any paras
+	/// assigned to occupied cores in `availability_cores` and `None` otherwise.
+	async fn candidate_pending_availability(
+		&self,
+		at: Hash,
+		para_id: Id,
+	) -> Result<Option<CommittedCandidateReceipt<Hash>>, ApiError>;
+
+	/// Get a vector of events concerning candidates that occurred within a block.
+	async fn candidate_events(&self, at: Hash) -> Result<Vec<CandidateEvent<Hash>>, ApiError>;
+
+	/// Get all the pending inbound messages in the downward message queue for a para.
+	async fn dmq_contents(
+		&self,
+		at: Hash,
+		recipient: Id,
+	) -> Result<Vec<InboundDownwardMessage<BlockNumber>>, ApiError>;
+
+	/// Get the contents of all channels addressed to the given recipient. Channels that have no
+	/// messages in them are also included.
+	async fn inbound_hrmp_channels_contents(
+		&self,
+		at: Hash,
+		recipient: Id,
+	) -> Result<BTreeMap<Id, Vec<InboundHrmpMessage<BlockNumber>>>, ApiError>;
+
+	/// Get the validation code from its hash.
+	async fn validation_code_by_hash(
+		&self,
+		at: Hash,
+		hash: ValidationCodeHash,
+	) -> Result<Option<ValidationCode>, ApiError>;
+
+	/// Scrape dispute relevant from on-chain, backing votes and resolved disputes.
+	async fn on_chain_votes(&self, at: Hash)
+		-> Result<Option<ScrapedOnChainVotes<Hash>>, ApiError>;
+
+	/***** Added in v2 *****/
+
+	/// Get the session info for the given session, if stored.
+	///
+	/// NOTE: This function is only available since parachain host version 2.
+	async fn session_info(
+		&self,
+		at: Hash,
+		index: SessionIndex,
+	) -> Result<Option<SessionInfo>, ApiError>;
+
+	/// Get the session info for the given session, if stored.
+	///
+	/// NOTE: This function is only available since parachain host version 2.
+	async fn session_info_before_version_2(
+		&self,
+		at: Hash,
+		index: SessionIndex,
+	) -> Result<Option<polkadot_primitives::v2::OldV1SessionInfo>, ApiError>;
+
+	/// Submits a PVF pre-checking statement into the transaction pool.
+	///
+	/// NOTE: This function is only available since parachain host version 2.
+	async fn submit_pvf_check_statement(
+		&self,
+		at: Hash,
+		stmt: PvfCheckStatement,
+		signature: ValidatorSignature,
+	) -> Result<(), ApiError>;
+
+	/// Returns code hashes of PVFs that require pre-checking by validators in the active set.
+	///
+	/// NOTE: This function is only available since parachain host version 2.
+	async fn pvfs_require_precheck(&self, at: Hash) -> Result<Vec<ValidationCodeHash>, ApiError>;
+
+	/// Fetch the hash of the validation code used by a para, making the given `OccupiedCoreAssumption`.
+	///
+	/// NOTE: This function is only available since parachain host version 2.
+	async fn validation_code_hash(
+		&self,
+		at: Hash,
+		para_id: Id,
+		assumption: OccupiedCoreAssumption,
+	) -> Result<Option<ValidationCodeHash>, ApiError>;
+
+	/// Returns all onchain disputes.
+	/// This is a staging method! Do not use on production runtimes!
+	async fn staging_get_disputes(
+		&self,
+		at: Hash,
+	) -> Result<Vec<(SessionIndex, CandidateHash, DisputeState<BlockNumber>)>, ApiError>;
+
+	// === BABE API ===
+
+	/// Returns information regarding the current epoch.
+	async fn current_epoch(&self, at: Hash) -> Result<Epoch, ApiError>;
+
+	// === AuthorityDiscovery API ===
+
+	/// Retrieve authority identifiers of the current and next authority set.
+	async fn authorities(
+		&self,
+		at: Hash,
+	) -> std::result::Result<Vec<sp_authority_discovery::AuthorityId>, ApiError>;
+}
+
+#[async_trait]
+impl<T> RuntimeApiSubsystemClient for T
+where
+	T: ProvideRuntimeApi<Block> + Send + Sync,
+	T::Api: ParachainHost<Block> + BabeApi<Block> + AuthorityDiscoveryApi<Block>,
+{
+	async fn validators(&self, at: Hash) -> Result<Vec<ValidatorId>, ApiError> {
+		self.runtime_api().validators(&BlockId::Hash(at))
+	}
+
+	async fn validator_groups(
+		&self,
+		at: Hash,
+	) -> Result<(Vec<Vec<ValidatorIndex>>, GroupRotationInfo<BlockNumber>), ApiError> {
+		self.runtime_api().validator_groups(&BlockId::Hash(at))
+	}
+
+	async fn availability_cores(
+		&self,
+		at: Hash,
+	) -> Result<Vec<CoreState<Hash, BlockNumber>>, ApiError> {
+		self.runtime_api().availability_cores(&BlockId::Hash(at))
+	}
+
+	async fn persisted_validation_data(
+		&self,
+		at: Hash,
+		para_id: Id,
+		assumption: OccupiedCoreAssumption,
+	) -> Result<Option<PersistedValidationData<Hash, BlockNumber>>, ApiError> {
+		self.runtime_api()
+			.persisted_validation_data(&BlockId::Hash(at), para_id, assumption)
+	}
+
+	async fn assumed_validation_data(
+		&self,
+		at: Hash,
+		para_id: Id,
+		expected_persisted_validation_data_hash: Hash,
+	) -> Result<Option<(PersistedValidationData<Hash, BlockNumber>, ValidationCodeHash)>, ApiError>
+	{
+		self.runtime_api().assumed_validation_data(
+			&BlockId::Hash(at),
+			para_id,
+			expected_persisted_validation_data_hash,
+		)
+	}
+
+	async fn check_validation_outputs(
+		&self,
+		at: Hash,
+		para_id: Id,
+		outputs: CandidateCommitments,
+	) -> Result<bool, ApiError> {
+		self.runtime_api()
+			.check_validation_outputs(&BlockId::Hash(at), para_id, outputs)
+	}
+
+	async fn session_index_for_child(&self, at: Hash) -> Result<SessionIndex, ApiError> {
+		self.runtime_api().session_index_for_child(&BlockId::Hash(at))
+	}
+
+	async fn validation_code(
+		&self,
+		at: Hash,
+		para_id: Id,
+		assumption: OccupiedCoreAssumption,
+	) -> Result<Option<ValidationCode>, ApiError> {
+		self.runtime_api().validation_code(&BlockId::Hash(at), para_id, assumption)
+	}
+
+	async fn candidate_pending_availability(
+		&self,
+		at: Hash,
+		para_id: Id,
+	) -> Result<Option<CommittedCandidateReceipt<Hash>>, ApiError> {
+		self.runtime_api().candidate_pending_availability(&BlockId::Hash(at), para_id)
+	}
+
+	async fn candidate_events(&self, at: Hash) -> Result<Vec<CandidateEvent<Hash>>, ApiError> {
+		self.runtime_api().candidate_events(&BlockId::Hash(at))
+	}
+
+	async fn dmq_contents(
+		&self,
+		at: Hash,
+		recipient: Id,
+	) -> Result<Vec<InboundDownwardMessage<BlockNumber>>, ApiError> {
+		self.runtime_api().dmq_contents(&BlockId::Hash(at), recipient)
+	}
+
+	async fn inbound_hrmp_channels_contents(
+		&self,
+		at: Hash,
+		recipient: Id,
+	) -> Result<BTreeMap<Id, Vec<InboundHrmpMessage<BlockNumber>>>, ApiError> {
+		self.runtime_api().inbound_hrmp_channels_contents(&BlockId::Hash(at), recipient)
+	}
+
+	async fn validation_code_by_hash(
+		&self,
+		at: Hash,
+		hash: ValidationCodeHash,
+	) -> Result<Option<ValidationCode>, ApiError> {
+		self.runtime_api().validation_code_by_hash(&BlockId::Hash(at), hash)
+	}
+
+	async fn on_chain_votes(
+		&self,
+		at: Hash,
+	) -> Result<Option<ScrapedOnChainVotes<Hash>>, ApiError> {
+		self.runtime_api().on_chain_votes(&BlockId::Hash(at))
+	}
+
+	async fn session_info(
+		&self,
+		at: Hash,
+		index: SessionIndex,
+	) -> Result<Option<SessionInfo>, ApiError> {
+		self.runtime_api().session_info(&BlockId::Hash(at), index)
+	}
+
+	async fn submit_pvf_check_statement(
+		&self,
+		at: Hash,
+		stmt: PvfCheckStatement,
+		signature: ValidatorSignature,
+	) -> Result<(), ApiError> {
+		self.runtime_api()
+			.submit_pvf_check_statement(&BlockId::Hash(at), stmt, signature)
+	}
+
+	async fn pvfs_require_precheck(&self, at: Hash) -> Result<Vec<ValidationCodeHash>, ApiError> {
+		self.runtime_api().pvfs_require_precheck(&BlockId::Hash(at))
+	}
+
+	async fn validation_code_hash(
+		&self,
+		at: Hash,
+		para_id: Id,
+		assumption: OccupiedCoreAssumption,
+	) -> Result<Option<ValidationCodeHash>, ApiError> {
+		self.runtime_api().validation_code_hash(&BlockId::Hash(at), para_id, assumption)
+	}
+
+	async fn current_epoch(&self, at: Hash) -> Result<Epoch, ApiError> {
+		self.runtime_api().current_epoch(&BlockId::Hash(at))
+	}
+
+	async fn authorities(
+		&self,
+		at: Hash,
+	) -> std::result::Result<Vec<sp_authority_discovery::AuthorityId>, ApiError> {
+		self.runtime_api().authorities(&BlockId::Hash(at))
+	}
+
+	async fn api_version_parachain_host(&self, at: Hash) -> Result<Option<u32>, ApiError> {
+		self.runtime_api().api_version::<dyn ParachainHost<Block>>(&BlockId::Hash(at))
+	}
+
+	#[warn(deprecated)]
+	async fn session_info_before_version_2(
+		&self,
+		at: Hash,
+		index: SessionIndex,
+	) -> Result<Option<polkadot_primitives::v2::OldV1SessionInfo>, ApiError> {
+		#[allow(deprecated)]
+		self.runtime_api().session_info_before_version_2(&BlockId::Hash(at), index)
+	}
+
+	async fn staging_get_disputes(
+		&self,
+		at: Hash,
+	) -> Result<Vec<(SessionIndex, CandidateHash, DisputeState<BlockNumber>)>, ApiError> {
+		self.runtime_api().staging_get_disputes(&BlockId::Hash(at))
+	}
+}
diff --git a/polkadot/rustfmt.toml b/polkadot/rustfmt.toml
index 9b872649d84..542c561edd4 100644
--- a/polkadot/rustfmt.toml
+++ b/polkadot/rustfmt.toml
@@ -21,3 +21,4 @@ use_field_init_shorthand = true
 ignore = [
     "bridges",
 ]
+edition = "2021"
-- 
GitLab