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::PendingSlashes)> {
379 disputes::slashing::Pallet::<T>::unapplied_slashes()
380}
381
382pub 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
395pub fn minimum_backing_votes<T: initializer::Config>() -> u32 {
397 configuration::ActiveConfig::<T>::get().minimum_backing_votes
398}
399
400pub fn backing_constraints<T: initializer::Config>(
402 para_id: ParaId,
403) -> Option<Constraints<BlockNumberFor<T>>> {
404 let config = configuration::ActiveConfig::<T>::get();
405 let now = frame_system::Pallet::<T>::block_number();
411
412 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 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#[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(¶_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, }
498 })
499 .collect()
500 })
501 .unwrap_or_else(|| vec![])
502 };
503
504 Some(BackingState { constraints, pending_availability })
505}
506
507#[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
513pub fn disabled_validators<T>() -> Vec<ValidatorIndex>
517where
518 T: shared::Config,
519{
520 <shared::Pallet<T>>::disabled_validators()
521}
522
523pub fn node_features<T: initializer::Config>() -> NodeFeatures {
525 configuration::ActiveConfig::<T>::get().node_features
526}
527
528pub fn approval_voting_params<T: initializer::Config>() -> ApprovalVotingParams {
530 configuration::ActiveConfig::<T>::get().approval_voting_params
531}
532
533pub fn claim_queue<T: scheduler::Config>() -> BTreeMap<CoreIndex, VecDeque<ParaId>> {
535 let config = configuration::ActiveConfig::<T>::get();
536 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
549pub 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
557pub 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
563pub fn scheduling_lookahead<T: initializer::Config>() -> u32 {
565 configuration::ActiveConfig::<T>::get().scheduler_params.lookahead
566}