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::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
45pub fn validators<T: initializer::Config>() -> Vec<ValidatorId> {
47 shared::ActiveValidatorKeys::<T>::get()
48}
49
50pub fn validator_groups<T: initializer::Config>(
52) -> (Vec<Vec<ValidatorIndex>>, GroupRotationInfo<BlockNumberFor<T>>) {
53 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
63pub 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 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
124fn 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
159pub 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
174pub 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 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 (!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 persisted_validation_data.zip(paras::CurrentCodeHash::<T>::get(¶_id))
203}
204
205pub 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
218pub fn session_index_for_child<T: initializer::Config>() -> SessionIndex {
220 shared::CurrentSessionIndex::<T>::get()
228}
229
230pub 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 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 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
259pub 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(¶_id))
265}
266
267#[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
278pub 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 RawEvent::<T>::UpwardMessagesReceived { .. } => return None,
301 RawEvent::<T>::__Ignore(_, _) => unreachable!("__Ignore cannot be used"),
302 })
303 })
304 .collect()
305}
306
307pub fn session_info<T: session_info::Config>(index: SessionIndex) -> Option<SessionInfo> {
309 session_info::Sessions::<T>::get(index)
310}
311
312pub fn dmq_contents<T: dmp::Config>(
314 recipient: ParaId,
315) -> Vec<InboundDownwardMessage<BlockNumberFor<T>>> {
316 dmp::Pallet::<T>::dmq_contents(recipient)
317}
318
319pub 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
326pub fn validation_code_by_hash<T: paras::Config>(
328 hash: ValidationCodeHash,
329) -> Option<ValidationCode> {
330 paras::CodeByHash::<T>::get(hash)
331}
332
333pub fn on_chain_votes<T: paras_inherent::Config>() -> Option<ScrapedOnChainVotes<T::Hash>> {
335 paras_inherent::OnChainVotes::<T>::get()
336}
337
338pub 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
346pub fn pvfs_require_precheck<T: paras::Config>() -> Vec<ValidationCodeHash> {
348 paras::Pallet::<T>::pvfs_require_precheck()
349}
350
351pub 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(¶_id))
361}
362
363pub fn get_session_disputes<T: disputes::Config>(
365) -> Vec<(SessionIndex, CandidateHash, DisputeState<BlockNumberFor<T>>)> {
366 disputes::Pallet::<T>::disputes()
367}
368
369pub 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
376pub 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
391pub fn unapplied_slashes_v2<T: disputes::slashing::Config>(
393) -> Vec<(SessionIndex, CandidateHash, slashing::PendingSlashes)> {
394 disputes::slashing::Pallet::<T>::unapplied_slashes()
395}
396
397pub 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
410pub fn minimum_backing_votes<T: initializer::Config>() -> u32 {
412 configuration::ActiveConfig::<T>::get().minimum_backing_votes
413}
414
415pub fn backing_constraints<T: initializer::Config>(
417 para_id: ParaId,
418) -> Option<Constraints<BlockNumberFor<T>>> {
419 let config = configuration::ActiveConfig::<T>::get();
420 let now = frame_system::Pallet::<T>::block_number();
426
427 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 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#[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(¶_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, }
513 })
514 .collect()
515 })
516 .unwrap_or_else(|| vec![])
517 };
518
519 Some(BackingState { constraints, pending_availability })
520}
521
522#[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
528pub fn disabled_validators<T>() -> Vec<ValidatorIndex>
532where
533 T: shared::Config,
534{
535 <shared::Pallet<T>>::disabled_validators()
536}
537
538pub fn node_features<T: initializer::Config>() -> NodeFeatures {
540 configuration::ActiveConfig::<T>::get().node_features
541}
542
543pub fn approval_voting_params<T: initializer::Config>() -> ApprovalVotingParams {
545 configuration::ActiveConfig::<T>::get().approval_voting_params
546}
547
548pub fn claim_queue<T: scheduler::Config>() -> BTreeMap<CoreIndex, VecDeque<ParaId>> {
550 let config = configuration::ActiveConfig::<T>::get();
551 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
564pub 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
572pub 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
578pub fn scheduling_lookahead<T: initializer::Config>() -> u32 {
580 configuration::ActiveConfig::<T>::get().scheduler_params.lookahead
581}