referrerpolicy=no-referrer-when-downgrade

polkadot_runtime_parachains/runtime_api_impl/
v13.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//! A module exporting runtime API implementation functions for all runtime APIs using `v5`
15//! primitives.
16//!
17//! Runtimes implementing the v13 runtime API are recommended to forward directly to these
18//! functions.
19
20use crate::{
21	configuration, disputes, dmp, hrmp, inclusion, initializer, paras, paras_inherent, scheduler,
22	session_info, shared,
23};
24use alloc::{
25	collections::{btree_map::BTreeMap, vec_deque::VecDeque},
26	vec,
27	vec::Vec,
28};
29use frame_support::traits::{GetStorageVersion, StorageVersion};
30use frame_system::pallet_prelude::*;
31use polkadot_primitives::{
32	async_backing::{
33		AsyncBackingParams, BackingState, CandidatePendingAvailability, Constraints,
34		InboundHrmpLimitations, OutboundHrmpChannelLimitations,
35	},
36	slashing, ApprovalVotingParams, AuthorityDiscoveryId, CandidateEvent, CandidateHash,
37	CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState,
38	ExecutorParams, GroupIndex, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage,
39	InboundHrmpMessage, NodeFeatures, OccupiedCore, OccupiedCoreAssumption,
40	PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo,
41	ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature,
42};
43use sp_runtime::traits::One;
44
45/// Implementation for the `validators` function of the runtime API.
46pub fn validators<T: initializer::Config>() -> Vec<ValidatorId> {
47	shared::ActiveValidatorKeys::<T>::get()
48}
49
50/// Implementation for the `validator_groups` function of the runtime API.
51pub fn validator_groups<T: initializer::Config>(
52) -> (Vec<Vec<ValidatorIndex>>, GroupRotationInfo<BlockNumberFor<T>>) {
53	// This formula needs to be the same as the one we use
54	// when populating group_responsible in `availability_cores`
55	let now = frame_system::Pallet::<T>::block_number() + One::one();
56
57	let groups = scheduler::ValidatorGroups::<T>::get();
58	let rotation_info = scheduler::Pallet::<T>::group_rotation_info(now);
59
60	(groups, rotation_info)
61}
62
63/// Implementation for the `availability_cores` function of the runtime API.
64pub fn availability_cores<T: initializer::Config>() -> Vec<CoreState<T::Hash, BlockNumberFor<T>>> {
65	let time_out_for = scheduler::Pallet::<T>::availability_timeout_predicate();
66
67	let group_responsible_for =
68		|backed_in_number, core_index| match scheduler::Pallet::<T>::group_assigned_to_core(
69			core_index,
70			backed_in_number,
71		) {
72			Some(g) => g,
73			None => {
74				log::warn!(
75					target: "runtime::polkadot-api::v2",
76					"Could not determine the group responsible for core extracted \
77					from list of cores for some prior block in same session",
78				);
79
80				GroupIndex(0)
81			},
82		};
83
84	let claim_queue = scheduler::Pallet::<T>::get_claim_queue();
85	let occupied_cores: BTreeMap<CoreIndex, inclusion::CandidatePendingAvailability<_, _>> =
86		inclusion::Pallet::<T>::get_occupied_cores().collect();
87	let n_cores = scheduler::Pallet::<T>::num_availability_cores();
88
89	(0..n_cores)
90		.map(|core_idx| {
91			let core_idx = CoreIndex(core_idx as u32);
92			if let Some(pending_availability) = occupied_cores.get(&core_idx) {
93				// Use the same block number for determining the responsible group as what the
94				// backing subsystem would use when it calls validator_groups api.
95				let backing_group_allocation_time =
96					pending_availability.relay_parent_number() + One::one();
97				CoreState::Occupied(OccupiedCore {
98					next_up_on_available: scheduler::Pallet::<T>::next_up_on_available(core_idx),
99					occupied_since: pending_availability.backed_in_number(),
100					time_out_at: time_out_for(pending_availability.backed_in_number()).live_until,
101					next_up_on_time_out: scheduler::Pallet::<T>::next_up_on_available(core_idx),
102					availability: pending_availability.availability_votes().clone(),
103					group_responsible: group_responsible_for(
104						backing_group_allocation_time,
105						pending_availability.core_occupied(),
106					),
107					candidate_hash: pending_availability.candidate_hash(),
108					candidate_descriptor: pending_availability.candidate_descriptor().clone(),
109				})
110			} else {
111				if let Some(assignment) = claim_queue.get(&core_idx).and_then(|q| q.front()) {
112					CoreState::Scheduled(polkadot_primitives::ScheduledCore {
113						para_id: assignment.para_id(),
114						collator: None,
115					})
116				} else {
117					CoreState::Free
118				}
119			}
120		})
121		.collect()
122}
123
124/// Returns current block number being processed and the corresponding root hash.
125fn current_relay_parent<T: frame_system::Config>(
126) -> (BlockNumberFor<T>, <T as frame_system::Config>::Hash) {
127	use codec::Decode as _;
128	let state_version = frame_system::Pallet::<T>::runtime_version().state_version();
129	let relay_parent_number = frame_system::Pallet::<T>::block_number();
130	let relay_parent_storage_root = T::Hash::decode(&mut &sp_io::storage::root(state_version)[..])
131		.expect("storage root must decode to the Hash type; qed");
132	(relay_parent_number, relay_parent_storage_root)
133}
134
135fn with_assumption<Config, T, F>(
136	para_id: ParaId,
137	assumption: OccupiedCoreAssumption,
138	build: F,
139) -> Option<T>
140where
141	Config: inclusion::Config,
142	F: FnOnce() -> Option<T>,
143{
144	match assumption {
145		OccupiedCoreAssumption::Included => {
146			<inclusion::Pallet<Config>>::force_enact(para_id);
147			build()
148		},
149		OccupiedCoreAssumption::TimedOut => build(),
150		OccupiedCoreAssumption::Free =>
151			if !<inclusion::Pallet<Config>>::candidates_pending_availability(para_id).is_empty() {
152				None
153			} else {
154				build()
155			},
156	}
157}
158
159/// Implementation for the `persisted_validation_data` function of the runtime API.
160pub fn persisted_validation_data<T: initializer::Config>(
161	para_id: ParaId,
162	assumption: OccupiedCoreAssumption,
163) -> Option<PersistedValidationData<T::Hash, BlockNumberFor<T>>> {
164	let (relay_parent_number, relay_parent_storage_root) = current_relay_parent::<T>();
165	with_assumption::<T, _, _>(para_id, assumption, || {
166		crate::util::make_persisted_validation_data::<T>(
167			para_id,
168			relay_parent_number,
169			relay_parent_storage_root,
170		)
171	})
172}
173
174/// Implementation for the `assumed_validation_data` function of the runtime API.
175pub fn assumed_validation_data<T: initializer::Config>(
176	para_id: ParaId,
177	expected_persisted_validation_data_hash: Hash,
178) -> Option<(PersistedValidationData<T::Hash, BlockNumberFor<T>>, ValidationCodeHash)> {
179	let (relay_parent_number, relay_parent_storage_root) = current_relay_parent::<T>();
180	// This closure obtains the `persisted_validation_data` for the given `para_id` and matches
181	// its hash against an expected one.
182	let make_validation_data = || {
183		crate::util::make_persisted_validation_data::<T>(
184			para_id,
185			relay_parent_number,
186			relay_parent_storage_root,
187		)
188		.filter(|validation_data| validation_data.hash() == expected_persisted_validation_data_hash)
189	};
190
191	let persisted_validation_data = make_validation_data().or_else(|| {
192		// Try again with force enacting the pending candidates. This check only makes sense if
193		// there are any pending candidates.
194		(!inclusion::Pallet::<T>::candidates_pending_availability(para_id).is_empty())
195			.then_some(())
196			.and_then(|_| {
197				inclusion::Pallet::<T>::force_enact(para_id);
198				make_validation_data()
199			})
200	});
201	// If we were successful, also query current validation code hash.
202	persisted_validation_data.zip(paras::CurrentCodeHash::<T>::get(&para_id))
203}
204
205/// Implementation for the `check_validation_outputs` function of the runtime API.
206pub fn check_validation_outputs<T: initializer::Config>(
207	para_id: ParaId,
208	outputs: polkadot_primitives::CandidateCommitments,
209) -> bool {
210	let relay_parent_number = frame_system::Pallet::<T>::block_number();
211	inclusion::Pallet::<T>::check_validation_outputs_for_runtime_api(
212		para_id,
213		relay_parent_number,
214		outputs,
215	)
216}
217
218/// Implementation for the `session_index_for_child` function of the runtime API.
219pub fn session_index_for_child<T: initializer::Config>() -> SessionIndex {
220	// Just returns the session index from `inclusion`. Runtime APIs follow
221	// initialization so the initializer will have applied any pending session change
222	// which is expected at the child of the block whose context the runtime API was invoked
223	// in.
224	//
225	// Incidentally, this is also the rationale for why it is OK to query validators or
226	// occupied cores or etc. and expect the correct response "for child".
227	shared::CurrentSessionIndex::<T>::get()
228}
229
230/// Implementation for the `AuthorityDiscoveryApi::authorities()` function of the runtime API.
231/// It is a heavy call, but currently only used for authority discovery, so it is fine.
232/// Gets next, current and some historical authority ids using `session_info` module.
233pub fn relevant_authority_ids<T: initializer::Config + pallet_authority_discovery::Config>(
234) -> Vec<AuthorityDiscoveryId> {
235	let current_session_index = session_index_for_child::<T>();
236	let earliest_stored_session = session_info::EarliestStoredSession::<T>::get();
237
238	// Due to `max_validators`, the `SessionInfo` stores only the validators who are actively
239	// selected to participate in parachain consensus. We'd like all authorities for the current
240	// and next sessions to be used in authority-discovery. The two sets likely have large overlap.
241	let mut authority_ids = pallet_authority_discovery::Pallet::<T>::current_authorities().to_vec();
242	authority_ids.extend(pallet_authority_discovery::Pallet::<T>::next_authorities().to_vec());
243
244	// Due to disputes, we'd like to remain connected to authorities of the previous few sessions.
245	// For this, we don't need anyone other than the validators actively participating in consensus.
246	for session_index in earliest_stored_session..current_session_index {
247		let info = session_info::Sessions::<T>::get(session_index);
248		if let Some(mut info) = info {
249			authority_ids.append(&mut info.discovery_keys);
250		}
251	}
252
253	authority_ids.sort();
254	authority_ids.dedup();
255
256	authority_ids
257}
258
259/// Implementation for the `validation_code` function of the runtime API.
260pub fn validation_code<T: initializer::Config>(
261	para_id: ParaId,
262	assumption: OccupiedCoreAssumption,
263) -> Option<ValidationCode> {
264	with_assumption::<T, _, _>(para_id, assumption, || paras::Pallet::<T>::current_code(&para_id))
265}
266
267/// Implementation for the `candidate_pending_availability` function of the runtime API.
268#[deprecated(
269	note = "`candidate_pending_availability` will be removed. Use `candidates_pending_availability` to query
270	all candidates pending availability"
271)]
272pub fn candidate_pending_availability<T: initializer::Config>(
273	para_id: ParaId,
274) -> Option<CommittedCandidateReceipt<T::Hash>> {
275	inclusion::Pallet::<T>::first_candidate_pending_availability(para_id)
276}
277
278/// Implementation for the `candidate_events` function of the runtime API.
279// NOTE: this runs without block initialization, as it accesses events.
280// this means it can run in a different session than other runtime APIs at the same block.
281pub fn candidate_events<T, F>(extract_event: F) -> Vec<CandidateEvent<T::Hash>>
282where
283	T: initializer::Config,
284	F: Fn(<T as frame_system::Config>::RuntimeEvent) -> Option<inclusion::Event<T>>,
285{
286	use inclusion::Event as RawEvent;
287
288	frame_system::Pallet::<T>::read_events_no_consensus()
289		.into_iter()
290		.filter_map(|record| extract_event(record.event))
291		.filter_map(|event| {
292			Some(match event {
293				RawEvent::<T>::CandidateBacked(c, h, core, group) =>
294					CandidateEvent::CandidateBacked(c, h, core, group),
295				RawEvent::<T>::CandidateIncluded(c, h, core, group) =>
296					CandidateEvent::CandidateIncluded(c, h, core, group),
297				RawEvent::<T>::CandidateTimedOut(c, h, core) =>
298					CandidateEvent::CandidateTimedOut(c, h, core),
299				// Not needed for candidate events.
300				RawEvent::<T>::UpwardMessagesReceived { .. } => return None,
301				RawEvent::<T>::__Ignore(_, _) => unreachable!("__Ignore cannot be used"),
302			})
303		})
304		.collect()
305}
306
307/// Get the session info for the given session, if stored.
308pub fn session_info<T: session_info::Config>(index: SessionIndex) -> Option<SessionInfo> {
309	session_info::Sessions::<T>::get(index)
310}
311
312/// Implementation for the `dmq_contents` function of the runtime API.
313pub fn dmq_contents<T: dmp::Config>(
314	recipient: ParaId,
315) -> Vec<InboundDownwardMessage<BlockNumberFor<T>>> {
316	dmp::Pallet::<T>::dmq_contents(recipient)
317}
318
319/// Implementation for the `inbound_hrmp_channels_contents` function of the runtime API.
320pub fn inbound_hrmp_channels_contents<T: hrmp::Config>(
321	recipient: ParaId,
322) -> BTreeMap<ParaId, Vec<InboundHrmpMessage<BlockNumberFor<T>>>> {
323	hrmp::Pallet::<T>::inbound_hrmp_channels_contents(recipient)
324}
325
326/// Implementation for the `validation_code_by_hash` function of the runtime API.
327pub fn validation_code_by_hash<T: paras::Config>(
328	hash: ValidationCodeHash,
329) -> Option<ValidationCode> {
330	paras::CodeByHash::<T>::get(hash)
331}
332
333/// Disputes imported via means of on-chain imports.
334pub fn on_chain_votes<T: paras_inherent::Config>() -> Option<ScrapedOnChainVotes<T::Hash>> {
335	paras_inherent::OnChainVotes::<T>::get()
336}
337
338/// Submits an PVF pre-checking vote.
339pub fn submit_pvf_check_statement<T: paras::Config>(
340	stmt: PvfCheckStatement,
341	signature: ValidatorSignature,
342) {
343	paras::Pallet::<T>::submit_pvf_check_statement(stmt, signature)
344}
345
346/// Returns the list of all PVF code hashes that require pre-checking.
347pub fn pvfs_require_precheck<T: paras::Config>() -> Vec<ValidationCodeHash> {
348	paras::Pallet::<T>::pvfs_require_precheck()
349}
350
351/// Returns the validation code hash for the given parachain making the given
352/// `OccupiedCoreAssumption`.
353pub fn validation_code_hash<T>(
354	para_id: ParaId,
355	assumption: OccupiedCoreAssumption,
356) -> Option<ValidationCodeHash>
357where
358	T: inclusion::Config,
359{
360	with_assumption::<T, _, _>(para_id, assumption, || paras::CurrentCodeHash::<T>::get(&para_id))
361}
362
363/// Implementation for `get_session_disputes` function from the runtime API
364pub fn get_session_disputes<T: disputes::Config>(
365) -> Vec<(SessionIndex, CandidateHash, DisputeState<BlockNumberFor<T>>)> {
366	disputes::Pallet::<T>::disputes()
367}
368
369/// Get session executor parameter set
370pub fn session_executor_params<T: session_info::Config>(
371	session_index: SessionIndex,
372) -> Option<ExecutorParams> {
373	session_info::SessionExecutorParams::<T>::get(session_index)
374}
375
376/// Implementation of `unapplied_slashes` runtime API
377pub fn unapplied_slashes<T: disputes::slashing::Config>(
378) -> Vec<(SessionIndex, CandidateHash, slashing::LegacyPendingSlashes)> {
379	disputes::slashing::Pallet::<T>::unapplied_slashes()
380		.into_iter()
381		.filter_map(|(session, candidate_hash, pending_slash)| {
382			let legacy_pending_slash = slashing::LegacyPendingSlashes {
383				keys: pending_slash.keys,
384				kind: pending_slash.kind.try_into().ok()?,
385			};
386			Some((session, candidate_hash, legacy_pending_slash))
387		})
388		.collect()
389}
390
391/// Implementation of `unapplied_slashes_v2` runtime API
392pub fn unapplied_slashes_v2<T: disputes::slashing::Config>(
393) -> Vec<(SessionIndex, CandidateHash, slashing::PendingSlashes)> {
394	disputes::slashing::Pallet::<T>::unapplied_slashes()
395}
396
397/// Implementation of `submit_report_dispute_lost` runtime API
398pub fn submit_unsigned_slashing_report<T: disputes::slashing::Config>(
399	dispute_proof: slashing::DisputeProof,
400	key_ownership_proof: slashing::OpaqueKeyOwnershipProof,
401) -> Option<()> {
402	let key_ownership_proof = key_ownership_proof.decode()?;
403
404	disputes::slashing::Pallet::<T>::submit_unsigned_slashing_report(
405		dispute_proof,
406		key_ownership_proof,
407	)
408}
409
410/// Return the min backing votes threshold from the configuration.
411pub fn minimum_backing_votes<T: initializer::Config>() -> u32 {
412	configuration::ActiveConfig::<T>::get().minimum_backing_votes
413}
414
415// Helper function that returns the backing constraints given a parachain id.
416pub fn backing_constraints<T: initializer::Config>(
417	para_id: ParaId,
418) -> Option<Constraints<BlockNumberFor<T>>> {
419	let config = configuration::ActiveConfig::<T>::get();
420	// Async backing is only expected to be enabled with a tracker capacity of 1.
421	// Subsequent configuration update gets applied on new session, which always
422	// clears the buffer.
423	//
424	// Thus, minimum relay parent is ensured to have asynchronous backing enabled.
425	let now = frame_system::Pallet::<T>::block_number();
426
427	// Use the right storage depending on version to ensure #64 doesn't cause issues with this
428	// migration.
429	let min_relay_parent_number = if shared::Pallet::<T>::on_chain_storage_version() ==
430		StorageVersion::new(0)
431	{
432		shared::migration::v0::AllowedRelayParents::<T>::get().hypothetical_earliest_block_number(
433			now,
434			config.scheduler_params.lookahead.saturating_sub(1),
435		)
436	} else {
437		shared::AllowedRelayParents::<T>::get().hypothetical_earliest_block_number(
438			now,
439			config.scheduler_params.lookahead.saturating_sub(1),
440		)
441	};
442
443	let required_parent = paras::Heads::<T>::get(para_id)?;
444	let validation_code_hash = paras::CurrentCodeHash::<T>::get(para_id)?;
445
446	let upgrade_restriction = paras::UpgradeRestrictionSignal::<T>::get(para_id);
447	let future_validation_code =
448		paras::FutureCodeUpgrades::<T>::get(para_id).and_then(|block_num| {
449			// Only read the storage if there's a pending upgrade.
450			Some(block_num).zip(paras::FutureCodeHash::<T>::get(para_id))
451		});
452
453	let (ump_msg_count, ump_total_bytes) =
454		inclusion::Pallet::<T>::relay_dispatch_queue_size(para_id);
455	let ump_remaining = config.max_upward_queue_count - ump_msg_count;
456	let ump_remaining_bytes = config.max_upward_queue_size - ump_total_bytes;
457
458	let dmp_remaining_messages = dmp::Pallet::<T>::dmq_contents(para_id)
459		.into_iter()
460		.map(|msg| msg.sent_at)
461		.collect();
462
463	let valid_watermarks = hrmp::Pallet::<T>::valid_watermarks(para_id);
464	let hrmp_inbound = InboundHrmpLimitations { valid_watermarks };
465	let hrmp_channels_out = hrmp::Pallet::<T>::outbound_remaining_capacity(para_id)
466		.into_iter()
467		.map(|(para, (messages_remaining, bytes_remaining))| {
468			(para, OutboundHrmpChannelLimitations { messages_remaining, bytes_remaining })
469		})
470		.collect();
471
472	Some(Constraints {
473		min_relay_parent_number,
474		max_pov_size: config.max_pov_size,
475		max_code_size: config.max_code_size,
476		max_head_data_size: Constraints::<BlockNumberFor<T>>::DEFAULT_MAX_HEAD_DATA_SIZE,
477		ump_remaining,
478		ump_remaining_bytes,
479		max_ump_num_per_candidate: config.max_upward_message_num_per_candidate,
480		dmp_remaining_messages,
481		hrmp_inbound,
482		hrmp_channels_out,
483		max_hrmp_num_per_candidate: config.hrmp_max_message_num_per_candidate,
484		required_parent,
485		validation_code_hash,
486		upgrade_restriction,
487		future_validation_code,
488	})
489}
490
491/// Implementation for `ParaBackingState` function from the runtime API
492#[deprecated(note = "`backing_state` will be removed. Use `backing_constraints` and
493	`candidates_pending_availability` instead.")]
494pub fn backing_state<T: initializer::Config>(
495	para_id: ParaId,
496) -> Option<BackingState<T::Hash, BlockNumberFor<T>>> {
497	let constraints = backing_constraints::<T>(para_id)?;
498
499	let pending_availability = {
500		crate::inclusion::PendingAvailability::<T>::get(&para_id)
501			.map(|pending_candidates| {
502				pending_candidates
503					.into_iter()
504					.map(|candidate| {
505						CandidatePendingAvailability {
506							candidate_hash: candidate.candidate_hash(),
507							descriptor: candidate.candidate_descriptor().clone(),
508							commitments: candidate.candidate_commitments().clone(),
509							relay_parent_number: candidate.relay_parent_number(),
510							max_pov_size: constraints.max_pov_size, /* assume always same in
511							                                         * session. */
512						}
513					})
514					.collect()
515			})
516			.unwrap_or_else(|| vec![])
517	};
518
519	Some(BackingState { constraints, pending_availability })
520}
521
522/// Implementation for `AsyncBackingParams` function from the runtime API
523#[deprecated = "AsyncBackingParams are going to be removed and ignored by relay chain validators, in favour of dynamically computed values based on the claim queue assignments"]
524pub fn async_backing_params<T: configuration::Config>() -> AsyncBackingParams {
525	configuration::ActiveConfig::<T>::get().async_backing_params
526}
527
528/// Implementation for `DisabledValidators`
529// CAVEAT: this should only be called on the node side
530// as it might produce incorrect results on session boundaries
531pub fn disabled_validators<T>() -> Vec<ValidatorIndex>
532where
533	T: shared::Config,
534{
535	<shared::Pallet<T>>::disabled_validators()
536}
537
538/// Returns the current state of the node features.
539pub fn node_features<T: initializer::Config>() -> NodeFeatures {
540	configuration::ActiveConfig::<T>::get().node_features
541}
542
543/// Approval voting subsystem configuration parameters
544pub fn approval_voting_params<T: initializer::Config>() -> ApprovalVotingParams {
545	configuration::ActiveConfig::<T>::get().approval_voting_params
546}
547
548/// Returns the claimqueue from the scheduler
549pub fn claim_queue<T: scheduler::Config>() -> BTreeMap<CoreIndex, VecDeque<ParaId>> {
550	let config = configuration::ActiveConfig::<T>::get();
551	// Extra sanity, config should already never be smaller than 1:
552	let n_lookahead = config.scheduler_params.lookahead.max(1);
553	scheduler::Pallet::<T>::get_claim_queue()
554		.into_iter()
555		.map(|(core_index, entries)| {
556			(
557				core_index,
558				entries.into_iter().map(|e| e.para_id()).take(n_lookahead as usize).collect(),
559			)
560		})
561		.collect()
562}
563
564/// Returns all the candidates that are pending availability for a given `ParaId`.
565/// Deprecates `candidate_pending_availability` in favor of supporting elastic scaling.
566pub fn candidates_pending_availability<T: initializer::Config>(
567	para_id: ParaId,
568) -> Vec<CommittedCandidateReceipt<T::Hash>> {
569	<inclusion::Pallet<T>>::candidates_pending_availability(para_id)
570}
571
572/// Implementation for `validation_code_bomb_limit` function from the runtime API
573pub fn validation_code_bomb_limit<T: initializer::Config>() -> u32 {
574	configuration::ActiveConfig::<T>::get().max_code_size *
575		configuration::MAX_VALIDATION_CODE_COMPRESSION_RATIO
576}
577
578/// Implementation for `scheduling_lookahead` function from the runtime API
579pub fn scheduling_lookahead<T: initializer::Config>() -> u32 {
580	configuration::ActiveConfig::<T>::get().scheduler_params.lookahead
581}