1use 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::{pallet_prelude::StorageVersion, traits::GetStorageVersion};
30use frame_system::pallet_prelude::*;
31use polkadot_primitives::{
32 async_backing::{
33 AsyncBackingParams, BackingState, CandidatePendingAvailability, Constraints,
34 InboundHrmpLimitations, OutboundHrmpChannelLimitations,
35 },
36 slashing, ApprovalVotingParams, AuthorityDiscoveryId, CandidateDescriptorVersion,
37 CandidateEvent, CandidateHash, CommittedCandidateReceiptV2 as CommittedCandidateReceipt,
38 CoreIndex, CoreState, DisputeState, ExecutorParams, GroupIndex, GroupRotationInfo, Hash,
39 Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, NodeFeatures, OccupiedCore,
40 OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScheduledCore,
41 ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash,
42 ValidatorId, ValidatorIndex, ValidatorSignature,
43};
44use sp_runtime::traits::One;
45
46pub fn validators<T: initializer::Config>() -> Vec<ValidatorId> {
48 shared::ActiveValidatorKeys::<T>::get()
49}
50
51pub fn validator_groups<T: initializer::Config>(
53) -> (Vec<Vec<ValidatorIndex>>, GroupRotationInfo<BlockNumberFor<T>>) {
54 let now = frame_system::Pallet::<T>::block_number() + One::one();
57
58 let groups = scheduler::ValidatorGroups::<T>::get();
59 let rotation_info = scheduler::Pallet::<T>::group_rotation_info(now);
60
61 (groups, rotation_info)
62}
63
64pub fn availability_cores<T: initializer::Config>() -> Vec<CoreState<T::Hash, BlockNumberFor<T>>> {
66 let time_out_for = scheduler::Pallet::<T>::availability_timeout_predicate();
67
68 let group_responsible_for =
69 |backed_in_number, core_index| match scheduler::Pallet::<T>::group_assigned_to_core(
70 core_index,
71 backed_in_number,
72 ) {
73 Some(g) => g,
74 None => {
75 log::warn!(
76 target: "runtime::polkadot-api::v2",
77 "Could not determine the group responsible for core extracted \
78 from list of cores for some prior block in same session",
79 );
80
81 GroupIndex(0)
82 },
83 };
84
85 let claim_queue = scheduler::Pallet::<T>::claim_queue();
86 let occupied_cores: BTreeMap<CoreIndex, inclusion::CandidatePendingAvailability<_, _>> =
87 inclusion::Pallet::<T>::get_occupied_cores().collect();
88 let n_cores = scheduler::Pallet::<T>::num_availability_cores();
89
90 (0..n_cores)
91 .map(|core_idx| {
92 let core_idx = CoreIndex(core_idx as u32);
93 if let Some(pending_availability) = occupied_cores.get(&core_idx) {
94 let scheduling_parent_number = if pending_availability
106 .candidate_descriptor()
107 .version() == CandidateDescriptorVersion::V3
108 {
109 let sp = pending_availability.candidate_descriptor().scheduling_parent();
110 let scheduling_parent_number = if shared::Pallet::<T>::on_chain_storage_version(
112 ) == StorageVersion::new(1)
113 {
114 shared::migration::v1::AllowedRelayParents::<T>::get().get_number(sp)
115 } else {
116 let session_index = shared::CurrentSessionIndex::<T>::get();
117 shared::Pallet::<T>::get_relay_parent_info(session_index, sp)
118 .map(|info| info.number)
119 };
120
121 scheduling_parent_number.unwrap_or_else(|| {
122 log::warn!(
123 target: "runtime::polkadot-api",
124 "Could not determine the scheduling parent of this v3 candidate {:?}",
125 pending_availability.candidate_hash()
126 );
127 pending_availability.relay_parent_number()
128 })
129 } else {
130 pending_availability.relay_parent_number()
131 };
132 let backing_group_allocation_time = scheduling_parent_number + One::one();
133 CoreState::Occupied(OccupiedCore {
134 next_up_on_available: claim_queue
135 .get(&core_idx)
136 .and_then(|q| q.front())
137 .map(|¶_id| ScheduledCore { para_id, collator: None }),
138 occupied_since: pending_availability.backed_in_number(),
139 time_out_at: time_out_for(pending_availability.backed_in_number()).live_until,
140 next_up_on_time_out: claim_queue
141 .get(&core_idx)
142 .and_then(|q| q.front())
143 .map(|¶_id| ScheduledCore { para_id, collator: None }),
144 availability: pending_availability.availability_votes().clone(),
145 group_responsible: group_responsible_for(
146 backing_group_allocation_time,
147 pending_availability.core_occupied(),
148 ),
149 candidate_hash: pending_availability.candidate_hash(),
150 candidate_descriptor: pending_availability.candidate_descriptor().clone(),
151 })
152 } else {
153 if let Some(¶_id) = claim_queue.get(&core_idx).and_then(|q| q.front()) {
154 CoreState::Scheduled(ScheduledCore { para_id, collator: None })
155 } else {
156 CoreState::Free
157 }
158 }
159 })
160 .collect()
161}
162
163fn current_relay_parent<T: frame_system::Config>(
165) -> (BlockNumberFor<T>, <T as frame_system::Config>::Hash) {
166 use codec::Decode as _;
167 let state_version = frame_system::Pallet::<T>::runtime_version().state_version();
168 let relay_parent_number = frame_system::Pallet::<T>::block_number();
169 let relay_parent_storage_root = T::Hash::decode(&mut &sp_io::storage::root(state_version)[..])
170 .expect("storage root must decode to the Hash type; qed");
171 (relay_parent_number, relay_parent_storage_root)
172}
173
174fn with_assumption<Config, T, F>(
175 para_id: ParaId,
176 assumption: OccupiedCoreAssumption,
177 build: F,
178) -> Option<T>
179where
180 Config: inclusion::Config,
181 F: FnOnce() -> Option<T>,
182{
183 match assumption {
184 OccupiedCoreAssumption::Included => {
185 <inclusion::Pallet<Config>>::force_enact(para_id);
186 build()
187 },
188 OccupiedCoreAssumption::TimedOut => build(),
189 OccupiedCoreAssumption::Free => {
190 if !<inclusion::Pallet<Config>>::candidates_pending_availability(para_id).is_empty() {
191 None
192 } else {
193 build()
194 }
195 },
196 }
197}
198
199pub fn persisted_validation_data<T: initializer::Config>(
201 para_id: ParaId,
202 assumption: OccupiedCoreAssumption,
203) -> Option<PersistedValidationData<T::Hash, BlockNumberFor<T>>> {
204 let (relay_parent_number, relay_parent_storage_root) = current_relay_parent::<T>();
205 with_assumption::<T, _, _>(para_id, assumption, || {
206 crate::util::make_persisted_validation_data::<T>(
207 para_id,
208 relay_parent_number,
209 relay_parent_storage_root,
210 )
211 })
212}
213
214pub fn assumed_validation_data<T: initializer::Config>(
216 para_id: ParaId,
217 expected_persisted_validation_data_hash: Hash,
218) -> Option<(PersistedValidationData<T::Hash, BlockNumberFor<T>>, ValidationCodeHash)> {
219 let (relay_parent_number, relay_parent_storage_root) = current_relay_parent::<T>();
220 let make_validation_data = || {
223 crate::util::make_persisted_validation_data::<T>(
224 para_id,
225 relay_parent_number,
226 relay_parent_storage_root,
227 )
228 .filter(|validation_data| validation_data.hash() == expected_persisted_validation_data_hash)
229 };
230
231 let persisted_validation_data = make_validation_data().or_else(|| {
232 (!inclusion::Pallet::<T>::candidates_pending_availability(para_id).is_empty())
235 .then_some(())
236 .and_then(|_| {
237 inclusion::Pallet::<T>::force_enact(para_id);
238 make_validation_data()
239 })
240 });
241 persisted_validation_data.zip(paras::CurrentCodeHash::<T>::get(¶_id))
243}
244
245pub fn check_validation_outputs<T: initializer::Config>(
247 para_id: ParaId,
248 outputs: polkadot_primitives::CandidateCommitments,
249) -> bool {
250 let relay_parent_number = frame_system::Pallet::<T>::block_number();
251 inclusion::Pallet::<T>::check_validation_outputs_for_runtime_api(
252 para_id,
253 relay_parent_number,
254 outputs,
255 )
256}
257
258pub fn session_index_for_child<T: initializer::Config>() -> SessionIndex {
260 shared::CurrentSessionIndex::<T>::get()
268}
269
270pub fn relevant_authority_ids<T: initializer::Config + pallet_authority_discovery::Config>(
274) -> Vec<AuthorityDiscoveryId> {
275 let current_session_index = session_index_for_child::<T>();
276 let earliest_stored_session = session_info::EarliestStoredSession::<T>::get();
277
278 let mut authority_ids = pallet_authority_discovery::Pallet::<T>::current_authorities().to_vec();
282 authority_ids.extend(pallet_authority_discovery::Pallet::<T>::next_authorities().to_vec());
283
284 for session_index in earliest_stored_session..current_session_index {
287 let info = session_info::Sessions::<T>::get(session_index);
288 if let Some(mut info) = info {
289 authority_ids.append(&mut info.discovery_keys);
290 }
291 }
292
293 authority_ids.sort();
294 authority_ids.dedup();
295
296 authority_ids
297}
298
299pub fn validation_code<T: initializer::Config>(
301 para_id: ParaId,
302 assumption: OccupiedCoreAssumption,
303) -> Option<ValidationCode> {
304 with_assumption::<T, _, _>(para_id, assumption, || paras::Pallet::<T>::current_code(¶_id))
305}
306
307#[deprecated(
309 note = "`candidate_pending_availability` will be removed. Use `candidates_pending_availability` to query
310 all candidates pending availability"
311)]
312pub fn candidate_pending_availability<T: initializer::Config>(
313 para_id: ParaId,
314) -> Option<CommittedCandidateReceipt<T::Hash>> {
315 inclusion::Pallet::<T>::first_candidate_pending_availability(para_id)
316}
317
318pub fn candidate_events<T, F>(extract_event: F) -> Vec<CandidateEvent<T::Hash>>
322where
323 T: initializer::Config,
324 F: Fn(<T as frame_system::Config>::RuntimeEvent) -> Option<inclusion::Event<T>>,
325{
326 use inclusion::Event as RawEvent;
327
328 frame_system::Pallet::<T>::read_events_no_consensus()
329 .into_iter()
330 .filter_map(|record| extract_event(record.event))
331 .filter_map(|event| {
332 Some(match event {
333 RawEvent::<T>::CandidateBacked(c, h, core, group) => {
334 CandidateEvent::CandidateBacked(c, h, core, group)
335 },
336 RawEvent::<T>::CandidateIncluded(c, h, core, group) => {
337 CandidateEvent::CandidateIncluded(c, h, core, group)
338 },
339 RawEvent::<T>::CandidateTimedOut(c, h, core) => {
340 CandidateEvent::CandidateTimedOut(c, h, core)
341 },
342 RawEvent::<T>::UpwardMessagesReceived { .. } => return None,
344 RawEvent::<T>::__Ignore(_, _) => unreachable!("__Ignore cannot be used"),
345 })
346 })
347 .collect()
348}
349
350pub fn session_info<T: session_info::Config>(index: SessionIndex) -> Option<SessionInfo> {
352 session_info::Sessions::<T>::get(index)
353}
354
355pub fn dmq_contents<T: dmp::Config>(
357 recipient: ParaId,
358) -> Vec<InboundDownwardMessage<BlockNumberFor<T>>> {
359 dmp::Pallet::<T>::dmq_contents(recipient)
360}
361
362pub fn inbound_hrmp_channels_contents<T: hrmp::Config>(
364 recipient: ParaId,
365) -> BTreeMap<ParaId, Vec<InboundHrmpMessage<BlockNumberFor<T>>>> {
366 hrmp::Pallet::<T>::inbound_hrmp_channels_contents(recipient)
367}
368
369pub fn validation_code_by_hash<T: paras::Config>(
371 hash: ValidationCodeHash,
372) -> Option<ValidationCode> {
373 paras::CodeByHash::<T>::get(hash)
374}
375
376pub fn on_chain_votes<T: paras_inherent::Config>() -> Option<ScrapedOnChainVotes<T::Hash>> {
378 paras_inherent::OnChainVotes::<T>::get()
379}
380
381pub fn submit_pvf_check_statement<T: paras::Config>(
383 stmt: PvfCheckStatement,
384 signature: ValidatorSignature,
385) {
386 paras::Pallet::<T>::submit_pvf_check_statement(stmt, signature)
387}
388
389pub fn pvfs_require_precheck<T: paras::Config>() -> Vec<ValidationCodeHash> {
391 paras::Pallet::<T>::pvfs_require_precheck()
392}
393
394pub fn validation_code_hash<T>(
397 para_id: ParaId,
398 assumption: OccupiedCoreAssumption,
399) -> Option<ValidationCodeHash>
400where
401 T: inclusion::Config,
402{
403 with_assumption::<T, _, _>(para_id, assumption, || paras::CurrentCodeHash::<T>::get(¶_id))
404}
405
406pub fn get_session_disputes<T: disputes::Config>(
408) -> Vec<(SessionIndex, CandidateHash, DisputeState<BlockNumberFor<T>>)> {
409 disputes::Pallet::<T>::disputes()
410}
411
412pub fn session_executor_params<T: session_info::Config>(
414 session_index: SessionIndex,
415) -> Option<ExecutorParams> {
416 session_info::SessionExecutorParams::<T>::get(session_index)
417}
418
419pub fn unapplied_slashes<T: disputes::slashing::Config>(
421) -> Vec<(SessionIndex, CandidateHash, slashing::LegacyPendingSlashes)> {
422 disputes::slashing::Pallet::<T>::unapplied_slashes()
423 .into_iter()
424 .filter_map(|(session, candidate_hash, pending_slash)| {
425 let legacy_pending_slash = slashing::LegacyPendingSlashes {
426 keys: pending_slash.keys,
427 kind: pending_slash.kind.try_into().ok()?,
428 };
429 Some((session, candidate_hash, legacy_pending_slash))
430 })
431 .collect()
432}
433
434pub fn unapplied_slashes_v2<T: disputes::slashing::Config>(
436) -> Vec<(SessionIndex, CandidateHash, slashing::PendingSlashes)> {
437 disputes::slashing::Pallet::<T>::unapplied_slashes()
438}
439
440pub fn submit_unsigned_slashing_report<T: disputes::slashing::Config>(
442 dispute_proof: slashing::DisputeProof,
443 key_ownership_proof: slashing::OpaqueKeyOwnershipProof,
444) -> Option<()> {
445 let key_ownership_proof = key_ownership_proof.decode()?;
446
447 disputes::slashing::Pallet::<T>::submit_unsigned_slashing_report(
448 dispute_proof,
449 key_ownership_proof,
450 )
451}
452
453pub fn minimum_backing_votes<T: initializer::Config>() -> u32 {
455 configuration::ActiveConfig::<T>::get().minimum_backing_votes
456}
457
458pub fn backing_constraints<T: initializer::Config>(
460 para_id: ParaId,
461) -> Option<Constraints<BlockNumberFor<T>>> {
462 let config = configuration::ActiveConfig::<T>::get();
463 let now = frame_system::Pallet::<T>::block_number();
464
465 let min_global_relay_parent_number = if shared::Pallet::<T>::on_chain_storage_version() ==
467 StorageVersion::new(1)
468 {
469 shared::migration::v1::AllowedRelayParents::<T>::get().hypothetical_earliest_block_number(
470 now,
471 config.scheduler_params.lookahead.saturating_sub(1),
472 )
473 } else {
474 shared::Pallet::<T>::get_minimum_relay_parent_number().unwrap_or(now)
475 };
476
477 let min_para_relay_parent_number = inclusion::Pallet::<T>::para_most_recent_context(¶_id)
478 .map(|ctx| core::cmp::max(ctx, min_global_relay_parent_number))
479 .unwrap_or(min_global_relay_parent_number);
480
481 let required_parent = paras::Heads::<T>::get(para_id)?;
482 let validation_code_hash = paras::CurrentCodeHash::<T>::get(para_id)?;
483
484 let upgrade_restriction = paras::UpgradeRestrictionSignal::<T>::get(para_id);
485 let future_validation_code =
486 paras::FutureCodeUpgrades::<T>::get(para_id).and_then(|block_num| {
487 Some(block_num).zip(paras::FutureCodeHash::<T>::get(para_id))
489 });
490
491 let (ump_msg_count, ump_total_bytes) =
492 inclusion::Pallet::<T>::relay_dispatch_queue_size(para_id);
493 let ump_remaining = config.max_upward_queue_count - ump_msg_count;
494 let ump_remaining_bytes = config.max_upward_queue_size - ump_total_bytes;
495
496 let dmp_remaining_messages = dmp::Pallet::<T>::dmq_contents(para_id)
497 .into_iter()
498 .map(|msg| msg.sent_at)
499 .collect();
500
501 let valid_watermarks = hrmp::Pallet::<T>::valid_watermarks(para_id);
502 let hrmp_inbound = InboundHrmpLimitations { valid_watermarks };
503 let hrmp_channels_out = hrmp::Pallet::<T>::outbound_remaining_capacity(para_id)
504 .into_iter()
505 .map(|(para, (messages_remaining, bytes_remaining))| {
506 (para, OutboundHrmpChannelLimitations { messages_remaining, bytes_remaining })
507 })
508 .collect();
509
510 Some(Constraints {
511 min_relay_parent_number: min_para_relay_parent_number,
512 max_pov_size: config.max_pov_size,
513 max_code_size: config.max_code_size,
514 max_head_data_size: Constraints::<BlockNumberFor<T>>::DEFAULT_MAX_HEAD_DATA_SIZE,
515 ump_remaining,
516 ump_remaining_bytes,
517 max_ump_num_per_candidate: config.max_upward_message_num_per_candidate,
518 dmp_remaining_messages,
519 hrmp_inbound,
520 hrmp_channels_out,
521 max_hrmp_num_per_candidate: config.hrmp_max_message_num_per_candidate,
522 required_parent,
523 validation_code_hash,
524 upgrade_restriction,
525 future_validation_code,
526 })
527}
528
529#[deprecated(note = "`backing_state` will be removed. Use `backing_constraints` and
531 `candidates_pending_availability` instead.")]
532pub fn backing_state<T: initializer::Config>(
533 para_id: ParaId,
534) -> Option<BackingState<T::Hash, BlockNumberFor<T>>> {
535 let constraints = backing_constraints::<T>(para_id)?;
536
537 let pending_availability = {
538 crate::inclusion::PendingAvailability::<T>::get(¶_id)
539 .map(|pending_candidates| {
540 pending_candidates
541 .into_iter()
542 .map(|candidate| {
543 CandidatePendingAvailability {
544 candidate_hash: candidate.candidate_hash(),
545 descriptor: candidate.candidate_descriptor().clone(),
546 commitments: candidate.candidate_commitments().clone(),
547 relay_parent_number: candidate.relay_parent_number(),
548 max_pov_size: constraints.max_pov_size, }
551 })
552 .collect()
553 })
554 .unwrap_or_else(|| vec![])
555 };
556
557 Some(BackingState { constraints, pending_availability })
558}
559
560#[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"]
562pub fn async_backing_params<T: configuration::Config>() -> AsyncBackingParams {
563 configuration::ActiveConfig::<T>::get().async_backing_params
564}
565
566pub fn disabled_validators<T>() -> Vec<ValidatorIndex>
570where
571 T: shared::Config,
572{
573 <shared::Pallet<T>>::disabled_validators()
574}
575
576pub fn node_features<T: initializer::Config>() -> NodeFeatures {
578 configuration::ActiveConfig::<T>::get().node_features
579}
580
581pub fn approval_voting_params<T: initializer::Config>() -> ApprovalVotingParams {
583 configuration::ActiveConfig::<T>::get().approval_voting_params
584}
585
586pub fn claim_queue<T: scheduler::Config>() -> BTreeMap<CoreIndex, VecDeque<ParaId>> {
588 scheduler::Pallet::<T>::claim_queue()
589}
590
591pub fn candidates_pending_availability<T: initializer::Config>(
594 para_id: ParaId,
595) -> Vec<CommittedCandidateReceipt<T::Hash>> {
596 <inclusion::Pallet<T>>::candidates_pending_availability(para_id)
597}
598
599pub fn validation_code_bomb_limit<T: initializer::Config>() -> u32 {
601 configuration::ActiveConfig::<T>::get().max_code_size *
602 configuration::MAX_VALIDATION_CODE_COMPRESSION_RATIO
603}
604
605pub fn scheduling_lookahead<T: initializer::Config>() -> u32 {
607 configuration::ActiveConfig::<T>::get().scheduler_params.lookahead
608}