Skip to main content

polkadot_node_core_runtime_api/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Implements the Runtime API Subsystem
18//!
19//! This provides a clean, ownerless wrapper around the parachain-related runtime APIs. This crate
20//! can also be used to cache responses from heavy runtime APIs.
21
22#![deny(unused_crate_dependencies)]
23#![warn(missing_docs)]
24
25use polkadot_node_subsystem::{
26	errors::RuntimeApiError,
27	messages::{RuntimeApiMessage, RuntimeApiRequest as Request},
28	overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, SubsystemResult,
29};
30use polkadot_node_subsystem_types::RuntimeApiSubsystemClient;
31use polkadot_primitives::Hash;
32
33use cache::{RequestResult, RequestResultCache};
34use futures::{channel::oneshot, prelude::*, select, stream::FuturesUnordered};
35use std::sync::Arc;
36
37mod cache;
38
39mod metrics;
40use self::metrics::Metrics;
41
42#[cfg(test)]
43mod tests;
44
45const LOG_TARGET: &str = "parachain::runtime-api";
46
47/// The number of maximum runtime API requests can be executed in parallel.
48/// Further requests will backpressure the bounded channel.
49const MAX_PARALLEL_REQUESTS: usize = 4;
50
51/// The name of the blocking task that executes a runtime API request.
52const API_REQUEST_TASK_NAME: &str = "polkadot-runtime-api-request";
53
54/// The `RuntimeApiSubsystem`. See module docs for more details.
55pub struct RuntimeApiSubsystem<Client> {
56	client: Arc<Client>,
57	metrics: Metrics,
58	spawn_handle: Box<dyn overseer::gen::Spawner>,
59	/// All the active runtime API requests that are currently being executed.
60	active_requests: FuturesUnordered<oneshot::Receiver<Option<RequestResult>>>,
61	/// Requests results cache
62	requests_cache: RequestResultCache,
63}
64
65impl<Client> RuntimeApiSubsystem<Client> {
66	/// Create a new Runtime API subsystem wrapping the given client and metrics.
67	pub fn new(
68		client: Arc<Client>,
69		metrics: Metrics,
70		spawner: impl overseer::gen::Spawner + 'static,
71	) -> Self {
72		RuntimeApiSubsystem {
73			client,
74			metrics,
75			spawn_handle: Box::new(spawner),
76			active_requests: Default::default(),
77			requests_cache: RequestResultCache::default(),
78		}
79	}
80}
81
82#[overseer::subsystem(RuntimeApi, error = SubsystemError, prefix = self::overseer)]
83impl<Client, Context> RuntimeApiSubsystem<Client>
84where
85	Client: RuntimeApiSubsystemClient + Send + Sync + 'static,
86{
87	fn start(self, ctx: Context) -> SpawnedSubsystem {
88		SpawnedSubsystem { future: run(ctx, self).boxed(), name: "runtime-api-subsystem" }
89	}
90}
91
92impl<Client> RuntimeApiSubsystem<Client>
93where
94	Client: RuntimeApiSubsystemClient + Send + 'static + Sync,
95{
96	fn store_cache(&mut self, result: RequestResult) {
97		use RequestResult::*;
98
99		match result {
100			Authorities(relay_parent, authorities) => {
101				self.requests_cache.cache_authorities(relay_parent, authorities)
102			},
103			Validators(relay_parent, validators) => {
104				self.requests_cache.cache_validators(relay_parent, validators)
105			},
106			MinimumBackingVotes(session_index, minimum_backing_votes) => self
107				.requests_cache
108				.cache_minimum_backing_votes(session_index, minimum_backing_votes),
109			ValidatorGroups(relay_parent, groups) => {
110				self.requests_cache.cache_validator_groups(relay_parent, groups)
111			},
112			AvailabilityCores(relay_parent, cores) => {
113				self.requests_cache.cache_availability_cores(relay_parent, cores)
114			},
115			PersistedValidationData(relay_parent, para_id, assumption, data) => self
116				.requests_cache
117				.cache_persisted_validation_data((relay_parent, para_id, assumption), data),
118			AssumedValidationData(
119				_relay_parent,
120				para_id,
121				expected_persisted_validation_data_hash,
122				data,
123			) => self.requests_cache.cache_assumed_validation_data(
124				(para_id, expected_persisted_validation_data_hash),
125				data,
126			),
127			CheckValidationOutputs(relay_parent, para_id, commitments, b) => self
128				.requests_cache
129				.cache_check_validation_outputs((relay_parent, para_id, commitments), b),
130			SessionIndexForChild(relay_parent, session_index) => {
131				self.requests_cache.cache_session_index_for_child(relay_parent, session_index)
132			},
133			ValidationCode(relay_parent, para_id, assumption, code) => self
134				.requests_cache
135				.cache_validation_code((relay_parent, para_id, assumption), code),
136			ValidationCodeByHash(_relay_parent, validation_code_hash, code) => {
137				self.requests_cache.cache_validation_code_by_hash(validation_code_hash, code)
138			},
139			CandidatePendingAvailability(relay_parent, para_id, candidate) => self
140				.requests_cache
141				.cache_candidate_pending_availability((relay_parent, para_id), candidate),
142			CandidatesPendingAvailability(relay_parent, para_id, candidates) => self
143				.requests_cache
144				.cache_candidates_pending_availability((relay_parent, para_id), candidates),
145			CandidateEvents(relay_parent, events) => {
146				self.requests_cache.cache_candidate_events(relay_parent, events)
147			},
148			SessionExecutorParams(_relay_parent, session_index, index) => {
149				self.requests_cache.cache_session_executor_params(session_index, index)
150			},
151			SessionInfo(_relay_parent, session_index, info) => {
152				if let Some(info) = info {
153					self.requests_cache.cache_session_info(session_index, info);
154				}
155			},
156			DmqContents(relay_parent, para_id, messages) => {
157				self.requests_cache.cache_dmq_contents((relay_parent, para_id), messages)
158			},
159			InboundHrmpChannelsContents(relay_parent, para_id, contents) => self
160				.requests_cache
161				.cache_inbound_hrmp_channel_contents((relay_parent, para_id), contents),
162			CurrentBabeEpoch(relay_parent, epoch) => {
163				self.requests_cache.cache_current_babe_epoch(relay_parent, epoch)
164			},
165			FetchOnChainVotes(relay_parent, scraped) => {
166				self.requests_cache.cache_on_chain_votes(relay_parent, scraped)
167			},
168			PvfsRequirePrecheck(relay_parent, pvfs) => {
169				self.requests_cache.cache_pvfs_require_precheck(relay_parent, pvfs)
170			},
171			SubmitPvfCheckStatement(()) => {},
172			ValidationCodeHash(relay_parent, para_id, assumption, hash) => self
173				.requests_cache
174				.cache_validation_code_hash((relay_parent, para_id, assumption), hash),
175			Version(relay_parent, version) => {
176				self.requests_cache.cache_version(relay_parent, version)
177			},
178			Disputes(relay_parent, disputes) => {
179				self.requests_cache.cache_disputes(relay_parent, disputes)
180			},
181			UnappliedSlashes(relay_parent, unapplied_slashes) => {
182				self.requests_cache.cache_unapplied_slashes(relay_parent, unapplied_slashes)
183			},
184			UnappliedSlashesV2(relay_parent, unapplied_slashes_v2) => self
185				.requests_cache
186				.cache_unapplied_slashes_v2(relay_parent, unapplied_slashes_v2),
187			KeyOwnershipProof(relay_parent, validator_id, key_ownership_proof) => self
188				.requests_cache
189				.cache_key_ownership_proof((relay_parent, validator_id), key_ownership_proof),
190			ApprovalVotingParams(_relay_parent, session_index, params) => {
191				self.requests_cache.cache_approval_voting_params(session_index, params)
192			},
193			SubmitReportDisputeLost(_) => {},
194			DisabledValidators(relay_parent, disabled_validators) => {
195				self.requests_cache.cache_disabled_validators(relay_parent, disabled_validators)
196			},
197			ParaBackingState(relay_parent, para_id, constraints) => self
198				.requests_cache
199				.cache_para_backing_state((relay_parent, para_id), constraints),
200			AsyncBackingParams(relay_parent, params) => {
201				self.requests_cache.cache_async_backing_params(relay_parent, params)
202			},
203			NodeFeatures(session_index, params) => {
204				self.requests_cache.cache_node_features(session_index, params)
205			},
206			ClaimQueue(relay_parent, sender) => {
207				self.requests_cache.cache_claim_queue(relay_parent, sender);
208			},
209			BackingConstraints(relay_parent, para_id, constraints) => self
210				.requests_cache
211				.cache_backing_constraints((relay_parent, para_id), constraints),
212			SchedulingLookahead(session_index, scheduling_lookahead) => self
213				.requests_cache
214				.cache_scheduling_lookahead(session_index, scheduling_lookahead),
215			ValidationCodeBombLimit(session_index, limit) => {
216				self.requests_cache.cache_validation_code_bomb_limit(session_index, limit)
217			},
218			ParaIds(session_index, para_ids) => {
219				self.requests_cache.cache_para_ids(session_index, para_ids);
220			},
221			MaxRelayParentSessionAge(session_index, max_relay_parent_session_age) => self
222				.requests_cache
223				.cache_max_relay_parent_session_age(session_index, max_relay_parent_session_age),
224			AncestorRelayParentInfo(relay_parent, session_index, queried_relay_parent, info) => {
225				self.requests_cache.cache_ancestor_relay_parent_info(
226					relay_parent,
227					session_index,
228					queried_relay_parent,
229					info,
230				)
231			},
232		}
233	}
234
235	fn query_cache(&mut self, relay_parent: Hash, request: Request) -> Option<Request> {
236		macro_rules! query {
237			// Just query by relay parent
238			($cache_api_name:ident (), $sender:expr) => {{
239				let sender = $sender;
240				if let Some(value) = self.requests_cache.$cache_api_name(&relay_parent) {
241					let _ = sender.send(Ok(value.clone()));
242					self.metrics.on_cached_request();
243					None
244				} else {
245					Some(sender)
246				}
247			}};
248			// Query by relay parent + additional parameters
249			($cache_api_name:ident ($($param:expr),+), $sender:expr) => {{
250				let sender = $sender;
251				if let Some(value) = self.requests_cache.$cache_api_name((relay_parent.clone(), $($param.clone()),+)) {
252					self.metrics.on_cached_request();
253					let _ = sender.send(Ok(value.clone()));
254					None
255				} else {
256					Some(sender)
257				}
258			}}
259		}
260
261		match request {
262			Request::Version(sender) => {
263				query!(version(), sender).map(|sender| Request::Version(sender))
264			},
265			Request::Authorities(sender) => {
266				query!(authorities(), sender).map(|sender| Request::Authorities(sender))
267			},
268			Request::Validators(sender) => {
269				query!(validators(), sender).map(|sender| Request::Validators(sender))
270			},
271			Request::ValidatorGroups(sender) => {
272				query!(validator_groups(), sender).map(|sender| Request::ValidatorGroups(sender))
273			},
274			Request::AvailabilityCores(sender) => query!(availability_cores(), sender)
275				.map(|sender| Request::AvailabilityCores(sender)),
276			Request::PersistedValidationData(para, assumption, sender) => {
277				query!(persisted_validation_data(para, assumption), sender)
278					.map(|sender| Request::PersistedValidationData(para, assumption, sender))
279			},
280			Request::AssumedValidationData(
281				para,
282				expected_persisted_validation_data_hash,
283				sender,
284			) => query!(
285				assumed_validation_data(para, expected_persisted_validation_data_hash),
286				sender
287			)
288			.map(|sender| {
289				Request::AssumedValidationData(
290					para,
291					expected_persisted_validation_data_hash,
292					sender,
293				)
294			}),
295			Request::CheckValidationOutputs(para, commitments, sender) => {
296				query!(check_validation_outputs(para, commitments), sender)
297					.map(|sender| Request::CheckValidationOutputs(para, commitments, sender))
298			},
299			Request::SessionIndexForChild(sender) => query!(session_index_for_child(), sender)
300				.map(|sender| Request::SessionIndexForChild(sender)),
301			Request::ValidationCode(para, assumption, sender) => {
302				query!(validation_code(para, assumption), sender)
303					.map(|sender| Request::ValidationCode(para, assumption, sender))
304			},
305			Request::ValidationCodeByHash(validation_code_hash, sender) => if let Some(code) = self
306				.requests_cache
307				.validation_code_by_hash((relay_parent, validation_code_hash))
308			{
309				self.metrics.on_cached_request();
310				let _ = sender.send(Ok(Some(code.clone())));
311				None
312			} else {
313				Some(sender)
314			}
315			.map(|sender| Request::ValidationCodeByHash(validation_code_hash, sender)),
316			Request::CandidatePendingAvailability(para, sender) => {
317				query!(candidate_pending_availability(para), sender)
318					.map(|sender| Request::CandidatePendingAvailability(para, sender))
319			},
320			Request::CandidatesPendingAvailability(para, sender) => {
321				query!(candidates_pending_availability(para), sender)
322					.map(|sender| Request::CandidatesPendingAvailability(para, sender))
323			},
324			Request::CandidateEvents(sender) => {
325				query!(candidate_events(), sender).map(|sender| Request::CandidateEvents(sender))
326			},
327			Request::SessionExecutorParams(session_index, sender) => {
328				if let Some(executor_params) =
329					self.requests_cache.session_executor_params(session_index)
330				{
331					self.metrics.on_cached_request();
332					let _ = sender.send(Ok(executor_params.clone()));
333					None
334				} else {
335					Some(Request::SessionExecutorParams(session_index, sender))
336				}
337			},
338			Request::SessionInfo(index, sender) => {
339				if let Some(info) = self.requests_cache.session_info(index) {
340					self.metrics.on_cached_request();
341					let _ = sender.send(Ok(Some(info.clone())));
342					None
343				} else {
344					Some(Request::SessionInfo(index, sender))
345				}
346			},
347			Request::DmqContents(id, sender) => {
348				query!(dmq_contents(id), sender).map(|sender| Request::DmqContents(id, sender))
349			},
350			Request::InboundHrmpChannelsContents(id, sender) => {
351				query!(inbound_hrmp_channels_contents(id), sender)
352					.map(|sender| Request::InboundHrmpChannelsContents(id, sender))
353			},
354			Request::CurrentBabeEpoch(sender) => {
355				query!(current_babe_epoch(), sender).map(|sender| Request::CurrentBabeEpoch(sender))
356			},
357			Request::FetchOnChainVotes(sender) => {
358				query!(on_chain_votes(), sender).map(|sender| Request::FetchOnChainVotes(sender))
359			},
360			Request::PvfsRequirePrecheck(sender) => query!(pvfs_require_precheck(), sender)
361				.map(|sender| Request::PvfsRequirePrecheck(sender)),
362			request @ Request::SubmitPvfCheckStatement(_, _, _) => {
363				// This request is side-effecting and thus cannot be cached.
364				Some(request)
365			},
366			Request::ValidationCodeHash(para, assumption, sender) => {
367				query!(validation_code_hash(para, assumption), sender)
368					.map(|sender| Request::ValidationCodeHash(para, assumption, sender))
369			},
370			Request::Disputes(sender) => {
371				query!(disputes(), sender).map(|sender| Request::Disputes(sender))
372			},
373			Request::UnappliedSlashes(sender) => {
374				query!(unapplied_slashes(), sender).map(|sender| Request::UnappliedSlashes(sender))
375			},
376			Request::UnappliedSlashesV2(sender) => query!(unapplied_slashes_v2(), sender)
377				.map(|sender| Request::UnappliedSlashesV2(sender)),
378			Request::KeyOwnershipProof(validator_id, sender) => {
379				query!(key_ownership_proof(validator_id), sender)
380					.map(|sender| Request::KeyOwnershipProof(validator_id, sender))
381			},
382			Request::SubmitReportDisputeLost(dispute_proof, key_ownership_proof, sender) => {
383				query!(submit_report_dispute_lost(dispute_proof, key_ownership_proof), sender).map(
384					|sender| {
385						Request::SubmitReportDisputeLost(dispute_proof, key_ownership_proof, sender)
386					},
387				)
388			},
389			Request::ApprovalVotingParams(session_index, sender) => {
390				query!(approval_voting_params(session_index), sender)
391					.map(|sender| Request::ApprovalVotingParams(session_index, sender))
392			},
393			Request::DisabledValidators(sender) => query!(disabled_validators(), sender)
394				.map(|sender| Request::DisabledValidators(sender)),
395			Request::ParaBackingState(para, sender) => query!(para_backing_state(para), sender)
396				.map(|sender| Request::ParaBackingState(para, sender)),
397			Request::AsyncBackingParams(sender) => query!(async_backing_params(), sender)
398				.map(|sender| Request::AsyncBackingParams(sender)),
399			Request::MinimumBackingVotes(index, sender) => {
400				if let Some(value) = self.requests_cache.minimum_backing_votes(index) {
401					self.metrics.on_cached_request();
402					let _ = sender.send(Ok(value));
403					None
404				} else {
405					Some(Request::MinimumBackingVotes(index, sender))
406				}
407			},
408			Request::NodeFeatures(index, sender) => {
409				if let Some(value) = self.requests_cache.node_features(index) {
410					self.metrics.on_cached_request();
411					let _ = sender.send(Ok(value.clone()));
412					None
413				} else {
414					Some(Request::NodeFeatures(index, sender))
415				}
416			},
417			Request::ClaimQueue(sender) => {
418				query!(claim_queue(), sender).map(|sender| Request::ClaimQueue(sender))
419			},
420			Request::BackingConstraints(para, sender) => query!(backing_constraints(para), sender)
421				.map(|sender| Request::BackingConstraints(para, sender)),
422			Request::SchedulingLookahead(index, sender) => {
423				if let Some(value) = self.requests_cache.scheduling_lookahead(index) {
424					self.metrics.on_cached_request();
425					let _ = sender.send(Ok(value));
426					None
427				} else {
428					Some(Request::SchedulingLookahead(index, sender))
429				}
430			},
431			Request::ValidationCodeBombLimit(index, sender) => {
432				if let Some(value) = self.requests_cache.validation_code_bomb_limit(index) {
433					self.metrics.on_cached_request();
434					let _ = sender.send(Ok(value));
435					None
436				} else {
437					Some(Request::ValidationCodeBombLimit(index, sender))
438				}
439			},
440			Request::ParaIds(index, sender) => {
441				if let Some(value) = self.requests_cache.para_ids(index) {
442					self.metrics.on_cached_request();
443					let _ = sender.send(Ok(value.clone()));
444					None
445				} else {
446					Some(Request::ParaIds(index, sender))
447				}
448			},
449			Request::MaxRelayParentSessionAge(index, sender) => {
450				if let Some(value) = self.requests_cache.max_relay_parent_session_age(index) {
451					self.metrics.on_cached_request();
452					let _ = sender.send(Ok(value));
453					None
454				} else {
455					Some(Request::MaxRelayParentSessionAge(index, sender))
456				}
457			},
458			Request::AncestorRelayParentInfo(session_index, queried_relay_parent, sender) => {
459				if let Some(value) = self.requests_cache.ancestor_relay_parent_info(
460					relay_parent,
461					session_index,
462					queried_relay_parent,
463				) {
464					self.metrics.on_cached_request();
465					let _ = sender.send(Ok(value.clone()));
466					None
467				} else {
468					Some(Request::AncestorRelayParentInfo(
469						session_index,
470						queried_relay_parent,
471						sender,
472					))
473				}
474			},
475		}
476	}
477
478	/// Spawn a runtime API request.
479	fn spawn_request(&mut self, relay_parent: Hash, request: Request) {
480		let client = self.client.clone();
481		let metrics = self.metrics.clone();
482		let (sender, receiver) = oneshot::channel();
483
484		// TODO: make the cache great again https://github.com/paritytech/polkadot/issues/5546
485		let request = match self.query_cache(relay_parent, request) {
486			Some(request) => request,
487			None => return,
488		};
489
490		let request = async move {
491			let result = make_runtime_api_request(client, metrics, relay_parent, request).await;
492			let _ = sender.send(result);
493		}
494		.boxed();
495
496		self.spawn_handle
497			.spawn_blocking(API_REQUEST_TASK_NAME, Some("runtime-api"), request);
498		self.active_requests.push(receiver);
499	}
500
501	/// Poll the active runtime API requests.
502	async fn poll_requests(&mut self) {
503		// If there are no active requests, this future should be pending forever.
504		if self.active_requests.len() == 0 {
505			return futures::pending!();
506		}
507
508		// If there are active requests, this will always resolve to `Some(_)` when a request is
509		// finished.
510		if let Some(Ok(Some(result))) = self.active_requests.next().await {
511			self.store_cache(result);
512		}
513	}
514
515	/// Returns true if our `active_requests` queue is full.
516	fn is_busy(&self) -> bool {
517		self.active_requests.len() >= MAX_PARALLEL_REQUESTS
518	}
519}
520
521#[overseer::contextbounds(RuntimeApi, prefix = self::overseer)]
522async fn run<Client, Context>(
523	mut ctx: Context,
524	mut subsystem: RuntimeApiSubsystem<Client>,
525) -> SubsystemResult<()>
526where
527	Client: RuntimeApiSubsystemClient + Send + Sync + 'static,
528{
529	loop {
530		// Let's add some back pressure when the subsystem is running at `MAX_PARALLEL_REQUESTS`.
531		// This can never block forever, because `active_requests` is owned by this task and any
532		// mutations happen either in `poll_requests` or `spawn_request` - so if `is_busy` returns
533		// true, then even if all of the requests finish before us calling `poll_requests` the
534		// `active_requests` length remains invariant.
535		if subsystem.is_busy() {
536			// Since we are not using any internal waiting queues, we need to wait for exactly
537			// one request to complete before we can read the next one from the overseer channel.
538			let _ = subsystem.poll_requests().await;
539		}
540
541		select! {
542			req = ctx.recv().fuse() => match req? {
543				FromOrchestra::Signal(OverseerSignal::Conclude) => return Ok(()),
544				FromOrchestra::Signal(OverseerSignal::ActiveLeaves(_)) => {},
545				FromOrchestra::Signal(OverseerSignal::BlockFinalized(..)) => {},
546				FromOrchestra::Communication { msg } => match msg {
547					RuntimeApiMessage::Request(relay_parent, request) => {
548						subsystem.spawn_request(relay_parent, request);
549					},
550				}
551			},
552			_ = subsystem.poll_requests().fuse() => {},
553		}
554	}
555}
556
557async fn make_runtime_api_request<Client>(
558	client: Arc<Client>,
559	metrics: Metrics,
560	relay_parent: Hash,
561	request: Request,
562) -> Option<RequestResult>
563where
564	Client: RuntimeApiSubsystemClient + 'static,
565{
566	let _timer = metrics.time_make_runtime_api_request();
567
568	macro_rules! query {
569		($req_variant:ident, $api_name:ident ($($param:expr),*), ver = $version:expr, $sender:expr) => {{
570			query!($req_variant, $api_name($($param),*), ver = $version, $sender, result = ( relay_parent $(, $param )* ) )
571		}};
572		($req_variant:ident, $api_name:ident ($($param:expr),*), ver = $version:expr, $sender:expr, result = ( $($results:expr),* ) ) => {{
573			let sender = $sender;
574			let version: u32 = $version; // enforce type for the version expression
575			let runtime_version = client.api_version_parachain_host(relay_parent).await
576				.unwrap_or_else(|e| {
577					gum::warn!(
578						target: LOG_TARGET,
579						api = ?stringify!($api_name),
580						"cannot query the runtime API version: {}",
581						e,
582					);
583					Some(0)
584				})
585				.unwrap_or_else(|| {
586					gum::warn!(
587						target: LOG_TARGET,
588						"no runtime version is reported"
589					);
590					0
591				});
592
593			let res = if runtime_version >= version {
594				client.$api_name(relay_parent $(, $param.clone() )*).await
595					.map_err(|e| RuntimeApiError::Execution {
596						runtime_api_name: stringify!($api_name),
597						source: std::sync::Arc::new(e),
598					})
599			} else {
600				Err(RuntimeApiError::NotSupported {
601					runtime_api_name: stringify!($api_name),
602				})
603			};
604			metrics.on_request(res.is_ok());
605			let _ = sender.send(res.clone());
606
607			res.ok().map(|res| RequestResult::$req_variant($( $results, )* res))
608		}}
609	}
610
611	match request {
612		Request::Version(sender) => {
613			let runtime_version = match client.api_version_parachain_host(relay_parent).await {
614				Ok(Some(v)) => Ok(v),
615				Ok(None) => Err(RuntimeApiError::NotSupported { runtime_api_name: "api_version" }),
616				Err(e) => Err(RuntimeApiError::Execution {
617					runtime_api_name: "api_version",
618					source: std::sync::Arc::new(e),
619				}),
620			};
621
622			let _ = sender.send(runtime_version.clone());
623			runtime_version.ok().map(|v| RequestResult::Version(relay_parent, v))
624		},
625
626		Request::Authorities(sender) => query!(Authorities, authorities(), ver = 1, sender),
627		Request::Validators(sender) => query!(Validators, validators(), ver = 1, sender),
628		Request::ValidatorGroups(sender) => {
629			query!(ValidatorGroups, validator_groups(), ver = 1, sender)
630		},
631		Request::AvailabilityCores(sender) => {
632			query!(AvailabilityCores, availability_cores(), ver = 1, sender)
633		},
634		Request::PersistedValidationData(para, assumption, sender) => query!(
635			PersistedValidationData,
636			persisted_validation_data(para, assumption),
637			ver = 1,
638			sender
639		),
640		Request::AssumedValidationData(para, expected_persisted_validation_data_hash, sender) => {
641			query!(
642				AssumedValidationData,
643				assumed_validation_data(para, expected_persisted_validation_data_hash),
644				ver = 1,
645				sender
646			)
647		},
648		Request::CheckValidationOutputs(para, commitments, sender) => query!(
649			CheckValidationOutputs,
650			check_validation_outputs(para, commitments),
651			ver = 1,
652			sender
653		),
654		Request::SessionIndexForChild(sender) => {
655			query!(SessionIndexForChild, session_index_for_child(), ver = 1, sender)
656		},
657		Request::ValidationCode(para, assumption, sender) => {
658			query!(ValidationCode, validation_code(para, assumption), ver = 1, sender)
659		},
660		Request::ValidationCodeByHash(validation_code_hash, sender) => query!(
661			ValidationCodeByHash,
662			validation_code_by_hash(validation_code_hash),
663			ver = 1,
664			sender
665		),
666		Request::CandidatePendingAvailability(para, sender) => query!(
667			CandidatePendingAvailability,
668			candidate_pending_availability(para),
669			ver = 1,
670			sender
671		),
672		Request::CandidatesPendingAvailability(para, sender) => query!(
673			CandidatesPendingAvailability,
674			candidates_pending_availability(para),
675			ver = Request::CANDIDATES_PENDING_AVAILABILITY_RUNTIME_REQUIREMENT,
676			sender
677		),
678		Request::CandidateEvents(sender) => {
679			query!(CandidateEvents, candidate_events(), ver = 1, sender)
680		},
681		Request::SessionInfo(index, sender) => {
682			query!(SessionInfo, session_info(index), ver = 2, sender)
683		},
684		Request::SessionExecutorParams(session_index, sender) => query!(
685			SessionExecutorParams,
686			session_executor_params(session_index),
687			ver = Request::EXECUTOR_PARAMS_RUNTIME_REQUIREMENT,
688			sender
689		),
690		Request::DmqContents(id, sender) => query!(DmqContents, dmq_contents(id), ver = 1, sender),
691		Request::InboundHrmpChannelsContents(id, sender) => {
692			query!(InboundHrmpChannelsContents, inbound_hrmp_channels_contents(id), ver = 1, sender)
693		},
694		Request::CurrentBabeEpoch(sender) => {
695			query!(CurrentBabeEpoch, current_epoch(), ver = 1, sender)
696		},
697		Request::FetchOnChainVotes(sender) => {
698			query!(FetchOnChainVotes, on_chain_votes(), ver = 1, sender)
699		},
700		Request::SubmitPvfCheckStatement(stmt, signature, sender) => {
701			query!(
702				SubmitPvfCheckStatement,
703				submit_pvf_check_statement(stmt, signature),
704				ver = 2,
705				sender,
706				result = ()
707			)
708		},
709		Request::PvfsRequirePrecheck(sender) => {
710			query!(PvfsRequirePrecheck, pvfs_require_precheck(), ver = 2, sender)
711		},
712		Request::ValidationCodeHash(para, assumption, sender) => {
713			query!(ValidationCodeHash, validation_code_hash(para, assumption), ver = 2, sender)
714		},
715		Request::Disputes(sender) => {
716			query!(Disputes, disputes(), ver = Request::DISPUTES_RUNTIME_REQUIREMENT, sender)
717		},
718		Request::UnappliedSlashes(sender) => query!(
719			UnappliedSlashes,
720			unapplied_slashes(),
721			ver = Request::UNAPPLIED_SLASHES_RUNTIME_REQUIREMENT,
722			sender
723		),
724		Request::KeyOwnershipProof(validator_id, sender) => query!(
725			KeyOwnershipProof,
726			key_ownership_proof(validator_id),
727			ver = Request::KEY_OWNERSHIP_PROOF_RUNTIME_REQUIREMENT,
728			sender
729		),
730		Request::ApprovalVotingParams(session_index, sender) => {
731			query!(
732				ApprovalVotingParams,
733				approval_voting_params(session_index),
734				ver = Request::APPROVAL_VOTING_PARAMS_REQUIREMENT,
735				sender
736			)
737		},
738		Request::SubmitReportDisputeLost(dispute_proof, key_ownership_proof, sender) => query!(
739			SubmitReportDisputeLost,
740			submit_report_dispute_lost(dispute_proof, key_ownership_proof),
741			ver = Request::SUBMIT_REPORT_DISPUTE_LOST_RUNTIME_REQUIREMENT,
742			sender,
743			result = ()
744		),
745		Request::MinimumBackingVotes(index, sender) => query!(
746			MinimumBackingVotes,
747			minimum_backing_votes(index),
748			ver = Request::MINIMUM_BACKING_VOTES_RUNTIME_REQUIREMENT,
749			sender,
750			result = (index)
751		),
752		Request::DisabledValidators(sender) => query!(
753			DisabledValidators,
754			disabled_validators(),
755			ver = Request::DISABLED_VALIDATORS_RUNTIME_REQUIREMENT,
756			sender
757		),
758		Request::ParaBackingState(para, sender) => {
759			query!(
760				ParaBackingState,
761				para_backing_state(para),
762				ver = Request::ASYNC_BACKING_STATE_RUNTIME_REQUIREMENT,
763				sender
764			)
765		},
766		Request::AsyncBackingParams(sender) => {
767			query!(
768				AsyncBackingParams,
769				async_backing_params(),
770				ver = Request::ASYNC_BACKING_STATE_RUNTIME_REQUIREMENT,
771				sender
772			)
773		},
774		Request::NodeFeatures(index, sender) => query!(
775			NodeFeatures,
776			node_features(),
777			ver = Request::NODE_FEATURES_RUNTIME_REQUIREMENT,
778			sender,
779			result = (index)
780		),
781		Request::ClaimQueue(sender) => query!(
782			ClaimQueue,
783			claim_queue(),
784			ver = Request::CLAIM_QUEUE_RUNTIME_REQUIREMENT,
785			sender
786		),
787		Request::BackingConstraints(para, sender) => {
788			query!(
789				BackingConstraints,
790				backing_constraints(para),
791				ver = Request::CONSTRAINTS_RUNTIME_REQUIREMENT,
792				sender
793			)
794		},
795		Request::SchedulingLookahead(index, sender) => query!(
796			SchedulingLookahead,
797			scheduling_lookahead(),
798			ver = Request::SCHEDULING_LOOKAHEAD_RUNTIME_REQUIREMENT,
799			sender,
800			result = (index)
801		),
802		Request::ValidationCodeBombLimit(index, sender) => query!(
803			ValidationCodeBombLimit,
804			validation_code_bomb_limit(),
805			ver = Request::VALIDATION_CODE_BOMB_LIMIT_RUNTIME_REQUIREMENT,
806			sender,
807			result = (index)
808		),
809		Request::ParaIds(index, sender) => query!(
810			ParaIds,
811			para_ids(),
812			ver = Request::PARAIDS_RUNTIME_REQUIREMENT,
813			sender,
814			result = (index)
815		),
816		Request::MaxRelayParentSessionAge(index, sender) => query!(
817			MaxRelayParentSessionAge,
818			max_relay_parent_session_age(),
819			ver = Request::MAX_RELAY_PARENT_SESSION_AGE_RUNTIME_REQUIREMENT,
820			sender,
821			result = (index)
822		),
823		Request::AncestorRelayParentInfo(session_index, queried_relay_parent, sender) => query!(
824			AncestorRelayParentInfo,
825			ancestor_relay_parent_info(session_index, queried_relay_parent),
826			ver = Request::ANCESTOR_RELAY_PARENT_INFO_RUNTIME_REQUIREMENT,
827			sender,
828			result = (relay_parent, session_index, queried_relay_parent)
829		),
830		Request::UnappliedSlashesV2(sender) => query!(
831			UnappliedSlashesV2,
832			unapplied_slashes_v2(),
833			ver = Request::UNAPPLIED_SLASHES_V2_RUNTIME_REQUIREMENT,
834			sender
835		),
836	}
837}