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::PendingSlashes)> {
379	disputes::slashing::Pallet::<T>::unapplied_slashes()
380}
381
382/// Implementation of `submit_report_dispute_lost` runtime API
383pub fn submit_unsigned_slashing_report<T: disputes::slashing::Config>(
384	dispute_proof: slashing::DisputeProof,
385	key_ownership_proof: slashing::OpaqueKeyOwnershipProof,
386) -> Option<()> {
387	let key_ownership_proof = key_ownership_proof.decode()?;
388
389	disputes::slashing::Pallet::<T>::submit_unsigned_slashing_report(
390		dispute_proof,
391		key_ownership_proof,
392	)
393}
394
395/// Return the min backing votes threshold from the configuration.
396pub fn minimum_backing_votes<T: initializer::Config>() -> u32 {
397	configuration::ActiveConfig::<T>::get().minimum_backing_votes
398}
399
400// Helper function that returns the backing constraints given a parachain id.
401pub fn backing_constraints<T: initializer::Config>(
402	para_id: ParaId,
403) -> Option<Constraints<BlockNumberFor<T>>> {
404	let config = configuration::ActiveConfig::<T>::get();
405	// Async backing is only expected to be enabled with a tracker capacity of 1.
406	// Subsequent configuration update gets applied on new session, which always
407	// clears the buffer.
408	//
409	// Thus, minimum relay parent is ensured to have asynchronous backing enabled.
410	let now = frame_system::Pallet::<T>::block_number();
411
412	// Use the right storage depending on version to ensure #64 doesn't cause issues with this
413	// migration.
414	let min_relay_parent_number = if shared::Pallet::<T>::on_chain_storage_version() ==
415		StorageVersion::new(0)
416	{
417		shared::migration::v0::AllowedRelayParents::<T>::get().hypothetical_earliest_block_number(
418			now,
419			config.scheduler_params.lookahead.saturating_sub(1),
420		)
421	} else {
422		shared::AllowedRelayParents::<T>::get().hypothetical_earliest_block_number(
423			now,
424			config.scheduler_params.lookahead.saturating_sub(1),
425		)
426	};
427
428	let required_parent = paras::Heads::<T>::get(para_id)?;
429	let validation_code_hash = paras::CurrentCodeHash::<T>::get(para_id)?;
430
431	let upgrade_restriction = paras::UpgradeRestrictionSignal::<T>::get(para_id);
432	let future_validation_code =
433		paras::FutureCodeUpgrades::<T>::get(para_id).and_then(|block_num| {
434			// Only read the storage if there's a pending upgrade.
435			Some(block_num).zip(paras::FutureCodeHash::<T>::get(para_id))
436		});
437
438	let (ump_msg_count, ump_total_bytes) =
439		inclusion::Pallet::<T>::relay_dispatch_queue_size(para_id);
440	let ump_remaining = config.max_upward_queue_count - ump_msg_count;
441	let ump_remaining_bytes = config.max_upward_queue_size - ump_total_bytes;
442
443	let dmp_remaining_messages = dmp::Pallet::<T>::dmq_contents(para_id)
444		.into_iter()
445		.map(|msg| msg.sent_at)
446		.collect();
447
448	let valid_watermarks = hrmp::Pallet::<T>::valid_watermarks(para_id);
449	let hrmp_inbound = InboundHrmpLimitations { valid_watermarks };
450	let hrmp_channels_out = hrmp::Pallet::<T>::outbound_remaining_capacity(para_id)
451		.into_iter()
452		.map(|(para, (messages_remaining, bytes_remaining))| {
453			(para, OutboundHrmpChannelLimitations { messages_remaining, bytes_remaining })
454		})
455		.collect();
456
457	Some(Constraints {
458		min_relay_parent_number,
459		max_pov_size: config.max_pov_size,
460		max_code_size: config.max_code_size,
461		max_head_data_size: Constraints::<BlockNumberFor<T>>::DEFAULT_MAX_HEAD_DATA_SIZE,
462		ump_remaining,
463		ump_remaining_bytes,
464		max_ump_num_per_candidate: config.max_upward_message_num_per_candidate,
465		dmp_remaining_messages,
466		hrmp_inbound,
467		hrmp_channels_out,
468		max_hrmp_num_per_candidate: config.hrmp_max_message_num_per_candidate,
469		required_parent,
470		validation_code_hash,
471		upgrade_restriction,
472		future_validation_code,
473	})
474}
475
476/// Implementation for `ParaBackingState` function from the runtime API
477#[deprecated(note = "`backing_state` will be removed. Use `backing_constraints` and
478	`candidates_pending_availability` instead.")]
479pub fn backing_state<T: initializer::Config>(
480	para_id: ParaId,
481) -> Option<BackingState<T::Hash, BlockNumberFor<T>>> {
482	let constraints = backing_constraints::<T>(para_id)?;
483
484	let pending_availability = {
485		crate::inclusion::PendingAvailability::<T>::get(&para_id)
486			.map(|pending_candidates| {
487				pending_candidates
488					.into_iter()
489					.map(|candidate| {
490						CandidatePendingAvailability {
491							candidate_hash: candidate.candidate_hash(),
492							descriptor: candidate.candidate_descriptor().clone(),
493							commitments: candidate.candidate_commitments().clone(),
494							relay_parent_number: candidate.relay_parent_number(),
495							max_pov_size: constraints.max_pov_size, /* assume always same in
496							                                         * session. */
497						}
498					})
499					.collect()
500			})
501			.unwrap_or_else(|| vec![])
502	};
503
504	Some(BackingState { constraints, pending_availability })
505}
506
507/// Implementation for `AsyncBackingParams` function from the runtime API
508#[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"]
509pub fn async_backing_params<T: configuration::Config>() -> AsyncBackingParams {
510	configuration::ActiveConfig::<T>::get().async_backing_params
511}
512
513/// Implementation for `DisabledValidators`
514// CAVEAT: this should only be called on the node side
515// as it might produce incorrect results on session boundaries
516pub fn disabled_validators<T>() -> Vec<ValidatorIndex>
517where
518	T: shared::Config,
519{
520	<shared::Pallet<T>>::disabled_validators()
521}
522
523/// Returns the current state of the node features.
524pub fn node_features<T: initializer::Config>() -> NodeFeatures {
525	configuration::ActiveConfig::<T>::get().node_features
526}
527
528/// Approval voting subsystem configuration parameters
529pub fn approval_voting_params<T: initializer::Config>() -> ApprovalVotingParams {
530	configuration::ActiveConfig::<T>::get().approval_voting_params
531}
532
533/// Returns the claimqueue from the scheduler
534pub fn claim_queue<T: scheduler::Config>() -> BTreeMap<CoreIndex, VecDeque<ParaId>> {
535	let config = configuration::ActiveConfig::<T>::get();
536	// Extra sanity, config should already never be smaller than 1:
537	let n_lookahead = config.scheduler_params.lookahead.max(1);
538	scheduler::Pallet::<T>::get_claim_queue()
539		.into_iter()
540		.map(|(core_index, entries)| {
541			(
542				core_index,
543				entries.into_iter().map(|e| e.para_id()).take(n_lookahead as usize).collect(),
544			)
545		})
546		.collect()
547}
548
549/// Returns all the candidates that are pending availability for a given `ParaId`.
550/// Deprecates `candidate_pending_availability` in favor of supporting elastic scaling.
551pub fn candidates_pending_availability<T: initializer::Config>(
552	para_id: ParaId,
553) -> Vec<CommittedCandidateReceipt<T::Hash>> {
554	<inclusion::Pallet<T>>::candidates_pending_availability(para_id)
555}
556
557/// Implementation for `validation_code_bomb_limit` function from the runtime API
558pub fn validation_code_bomb_limit<T: initializer::Config>() -> u32 {
559	configuration::ActiveConfig::<T>::get().max_code_size *
560		configuration::MAX_VALIDATION_CODE_COMPRESSION_RATIO
561}
562
563/// Implementation for `scheduling_lookahead` function from the runtime API
564pub fn scheduling_lookahead<T: initializer::Config>() -> u32 {
565	configuration::ActiveConfig::<T>::get().scheduler_params.lookahead
566}