1use polkadot_node_primitives::MAX_FINALITY_LAG;
20use schnellru::{ByLength, LruMap};
21
22use codec::Encode;
23use sp_application_crypto::AppCrypto;
24use sp_core::crypto::ByteArray;
25use sp_keystore::{Keystore, KeystorePtr};
26
27use polkadot_node_subsystem::{
28 errors::RuntimeApiError,
29 messages::{RuntimeApiMessage, RuntimeApiRequest},
30 overseer, SubsystemSender,
31};
32use polkadot_node_subsystem_types::UnpinHandle;
33use polkadot_primitives::{
34 node_features::FeatureIndex, slashing, CandidateEvent, CandidateHash, CoreIndex, CoreState,
35 EncodeAs, ExecutorParams, GroupIndex, GroupRotationInfo, Hash, Id as ParaId, IndexedVec,
36 NodeFeatures, OccupiedCore, ScrapedOnChainVotes, SessionIndex, SessionInfo, Signed,
37 SigningContext, UncheckedSigned, ValidationCode, ValidationCodeHash, ValidatorId,
38 ValidatorIndex, DEFAULT_SCHEDULING_LOOKAHEAD,
39};
40
41use std::collections::{BTreeMap, VecDeque};
42
43use crate::{
44 request_availability_cores, request_candidate_events, request_claim_queue,
45 request_disabled_validators, request_from_runtime, request_key_ownership_proof,
46 request_node_features, request_on_chain_votes, request_session_executor_params,
47 request_session_index_for_child, request_session_info, request_submit_report_dispute_lost,
48 request_unapplied_slashes, request_validation_code_by_hash, request_validator_groups,
49};
50
51mod error;
53
54use error::Result;
55pub use error::{recv_runtime, Error, FatalError, JfyiError};
56
57const LOG_TARGET: &'static str = "parachain::runtime-info";
58
59pub struct Config {
61 pub keystore: Option<KeystorePtr>,
65
66 pub session_cache_lru_size: u32,
68}
69
70pub struct RuntimeInfo {
74 session_index_cache: LruMap<Hash, SessionIndex>,
79
80 disabled_validators_cache: LruMap<Hash, Vec<ValidatorIndex>>,
84
85 session_info_cache: LruMap<SessionIndex, ExtendedSessionInfo>,
87
88 pinned_blocks: LruMap<SessionIndex, UnpinHandle>,
91
92 keystore: Option<KeystorePtr>,
94}
95
96pub struct ExtendedSessionInfo {
98 pub session_info: SessionInfo,
100 pub validator_info: ValidatorInfo,
102 pub executor_params: ExecutorParams,
104 pub node_features: NodeFeatures,
106}
107
108pub struct ValidatorInfo {
112 pub our_index: Option<ValidatorIndex>,
114 pub our_group: Option<GroupIndex>,
116}
117
118impl Default for Config {
119 fn default() -> Self {
120 Self {
121 keystore: None,
122 session_cache_lru_size: 2,
124 }
125 }
126}
127
128impl RuntimeInfo {
129 pub fn new(keystore: Option<KeystorePtr>) -> Self {
131 Self::new_with_config(Config { keystore, ..Default::default() })
132 }
133
134 pub fn new_with_config(cfg: Config) -> Self {
136 Self {
137 session_index_cache: LruMap::new(ByLength::new(
141 cfg.session_cache_lru_size.max(2 * MAX_FINALITY_LAG),
142 )),
143 session_info_cache: LruMap::new(ByLength::new(cfg.session_cache_lru_size)),
144 disabled_validators_cache: LruMap::new(ByLength::new(100)),
145 pinned_blocks: LruMap::new(ByLength::new(cfg.session_cache_lru_size)),
146 keystore: cfg.keystore,
147 }
148 }
149
150 pub async fn get_session_index_for_child<Sender>(
153 &mut self,
154 sender: &mut Sender,
155 parent: Hash,
156 ) -> Result<SessionIndex>
157 where
158 Sender: SubsystemSender<RuntimeApiMessage>,
159 {
160 match self.session_index_cache.get(&parent) {
161 Some(index) => Ok(*index),
162 None => {
163 let index =
164 recv_runtime(request_session_index_for_child(parent, sender).await).await?;
165 self.session_index_cache.insert(parent, index);
166 Ok(index)
167 },
168 }
169 }
170
171 pub fn pin_block(&mut self, session_index: SessionIndex, unpin_handle: UnpinHandle) {
174 self.pinned_blocks.get_or_insert(session_index, || unpin_handle);
175 }
176
177 pub fn get_block_in_session(&self, session_index: SessionIndex) -> Option<Hash> {
179 self.pinned_blocks.peek(&session_index).map(|h| h.hash())
180 }
181
182 pub async fn get_session_info<'a, Sender>(
184 &'a mut self,
185 sender: &mut Sender,
186 relay_parent: Hash,
187 ) -> Result<&'a ExtendedSessionInfo>
188 where
189 Sender: SubsystemSender<RuntimeApiMessage>,
190 {
191 let session_index = self.get_session_index_for_child(sender, relay_parent).await?;
192
193 self.get_session_info_by_index(sender, relay_parent, session_index).await
194 }
195
196 pub async fn get_disabled_validators<Sender>(
198 &mut self,
199 sender: &mut Sender,
200 relay_parent: Hash,
201 ) -> Result<Vec<ValidatorIndex>>
202 where
203 Sender: SubsystemSender<RuntimeApiMessage>,
204 {
205 match self.disabled_validators_cache.get(&relay_parent).cloned() {
206 Some(result) => Ok(result),
207 None => {
208 let disabled_validators =
209 request_disabled_validators(relay_parent, sender).await.await??;
210 self.disabled_validators_cache.insert(relay_parent, disabled_validators.clone());
211 Ok(disabled_validators)
212 },
213 }
214 }
215
216 pub async fn get_session_info_by_index<'a, Sender>(
221 &'a mut self,
222 sender: &mut Sender,
223 parent: Hash,
224 session_index: SessionIndex,
225 ) -> Result<&'a ExtendedSessionInfo>
226 where
227 Sender: SubsystemSender<RuntimeApiMessage>,
228 {
229 if self.session_info_cache.get(&session_index).is_none() {
230 let session_info =
231 recv_runtime(request_session_info(parent, session_index, sender).await)
232 .await?
233 .ok_or(JfyiError::NoSuchSession(session_index))?;
234
235 let executor_params =
236 recv_runtime(request_session_executor_params(parent, session_index, sender).await)
237 .await?
238 .ok_or(JfyiError::NoExecutorParams(session_index))?;
239
240 let validator_info = self.get_validator_info(&session_info)?;
241
242 let node_features =
243 request_node_features(parent, session_index, sender).await.await??;
244 let last_set_index = node_features.iter_ones().last().unwrap_or_default();
245 if last_set_index >= FeatureIndex::FirstUnassigned as usize {
246 gum::warn!(target: LOG_TARGET, "Runtime requires feature bit {} that node doesn't support, please upgrade node version", last_set_index);
247 }
248
249 let full_info = ExtendedSessionInfo {
250 session_info,
251 validator_info,
252 executor_params,
253 node_features,
254 };
255
256 self.session_info_cache.insert(session_index, full_info);
257 }
258 Ok(self
259 .session_info_cache
260 .get(&session_index)
261 .expect("We just put the value there. qed."))
262 }
263
264 pub async fn check_signature<Sender, Payload, RealPayload>(
266 &mut self,
267 sender: &mut Sender,
268 relay_parent: Hash,
269 signed: UncheckedSigned<Payload, RealPayload>,
270 ) -> Result<
271 std::result::Result<Signed<Payload, RealPayload>, UncheckedSigned<Payload, RealPayload>>,
272 >
273 where
274 Sender: SubsystemSender<RuntimeApiMessage>,
275 Payload: EncodeAs<RealPayload> + Clone,
276 RealPayload: Encode + Clone,
277 {
278 let session_index = self.get_session_index_for_child(sender, relay_parent).await?;
279 let info = self.get_session_info_by_index(sender, relay_parent, session_index).await?;
280 Ok(check_signature(session_index, &info.session_info, relay_parent, signed))
281 }
282
283 fn get_validator_info(&self, session_info: &SessionInfo) -> Result<ValidatorInfo> {
288 if let Some(our_index) = self.get_our_index(&session_info.validators) {
289 let our_group =
291 session_info.validator_groups.iter().enumerate().find_map(|(i, g)| {
292 g.iter().find_map(|v| {
293 if *v == our_index {
294 Some(GroupIndex(i as u32))
295 } else {
296 None
297 }
298 })
299 });
300 let info = ValidatorInfo { our_index: Some(our_index), our_group };
301 return Ok(info)
302 }
303 return Ok(ValidatorInfo { our_index: None, our_group: None })
304 }
305
306 fn get_our_index(
310 &self,
311 validators: &IndexedVec<ValidatorIndex, ValidatorId>,
312 ) -> Option<ValidatorIndex> {
313 let keystore = self.keystore.as_ref()?;
314 for (i, v) in validators.iter().enumerate() {
315 if Keystore::has_keys(&**keystore, &[(v.to_raw_vec(), ValidatorId::ID)]) {
316 return Some(ValidatorIndex(i as u32))
317 }
318 }
319 None
320 }
321}
322
323pub fn check_signature<Payload, RealPayload>(
325 session_index: SessionIndex,
326 session_info: &SessionInfo,
327 relay_parent: Hash,
328 signed: UncheckedSigned<Payload, RealPayload>,
329) -> std::result::Result<Signed<Payload, RealPayload>, UncheckedSigned<Payload, RealPayload>>
330where
331 Payload: EncodeAs<RealPayload> + Clone,
332 RealPayload: Encode + Clone,
333{
334 let signing_context = SigningContext { session_index, parent_hash: relay_parent };
335
336 session_info
337 .validators
338 .get(signed.unchecked_validator_index())
339 .ok_or_else(|| signed.clone())
340 .and_then(|v| signed.try_into_checked(&signing_context, v))
341}
342
343pub async fn get_availability_cores<Sender>(
345 sender: &mut Sender,
346 relay_parent: Hash,
347) -> Result<Vec<CoreState>>
348where
349 Sender: overseer::SubsystemSender<RuntimeApiMessage>,
350{
351 recv_runtime(request_availability_cores(relay_parent, sender).await).await
352}
353
354pub async fn get_occupied_cores<Sender>(
356 sender: &mut Sender,
357 relay_parent: Hash,
358) -> Result<Vec<(CoreIndex, OccupiedCore)>>
359where
360 Sender: overseer::SubsystemSender<RuntimeApiMessage>,
361{
362 let cores = get_availability_cores(sender, relay_parent).await?;
363
364 Ok(cores
365 .into_iter()
366 .enumerate()
367 .filter_map(|(core_index, core_state)| {
368 if let CoreState::Occupied(occupied) = core_state {
369 Some((CoreIndex(core_index as u32), occupied))
370 } else {
371 None
372 }
373 })
374 .collect())
375}
376
377pub async fn get_group_rotation_info<Sender>(
379 sender: &mut Sender,
380 relay_parent: Hash,
381) -> Result<GroupRotationInfo>
382where
383 Sender: overseer::SubsystemSender<RuntimeApiMessage>,
384{
385 let (_, info) = recv_runtime(request_validator_groups(relay_parent, sender).await).await?;
388 Ok(info)
389}
390
391pub async fn get_candidate_events<Sender>(
393 sender: &mut Sender,
394 relay_parent: Hash,
395) -> Result<Vec<CandidateEvent>>
396where
397 Sender: SubsystemSender<RuntimeApiMessage>,
398{
399 recv_runtime(request_candidate_events(relay_parent, sender).await).await
400}
401
402pub async fn get_on_chain_votes<Sender>(
404 sender: &mut Sender,
405 relay_parent: Hash,
406) -> Result<Option<ScrapedOnChainVotes>>
407where
408 Sender: SubsystemSender<RuntimeApiMessage>,
409{
410 recv_runtime(request_on_chain_votes(relay_parent, sender).await).await
411}
412
413pub async fn get_validation_code_by_hash<Sender>(
415 sender: &mut Sender,
416 relay_parent: Hash,
417 validation_code_hash: ValidationCodeHash,
418) -> Result<Option<ValidationCode>>
419where
420 Sender: SubsystemSender<RuntimeApiMessage>,
421{
422 recv_runtime(request_validation_code_by_hash(relay_parent, validation_code_hash, sender).await)
423 .await
424}
425
426pub async fn get_unapplied_slashes<Sender>(
428 sender: &mut Sender,
429 relay_parent: Hash,
430) -> Result<Vec<(SessionIndex, CandidateHash, slashing::PendingSlashes)>>
431where
432 Sender: SubsystemSender<RuntimeApiMessage>,
433{
434 recv_runtime(request_unapplied_slashes(relay_parent, sender).await).await
435}
436
437pub async fn key_ownership_proof<Sender>(
442 sender: &mut Sender,
443 relay_parent: Hash,
444 validator_id: ValidatorId,
445) -> Result<Option<slashing::OpaqueKeyOwnershipProof>>
446where
447 Sender: SubsystemSender<RuntimeApiMessage>,
448{
449 recv_runtime(request_key_ownership_proof(relay_parent, validator_id, sender).await).await
450}
451
452pub async fn submit_report_dispute_lost<Sender>(
454 sender: &mut Sender,
455 relay_parent: Hash,
456 dispute_proof: slashing::DisputeProof,
457 key_ownership_proof: slashing::OpaqueKeyOwnershipProof,
458) -> Result<Option<()>>
459where
460 Sender: SubsystemSender<RuntimeApiMessage>,
461{
462 recv_runtime(
463 request_submit_report_dispute_lost(
464 relay_parent,
465 dispute_proof,
466 key_ownership_proof,
467 sender,
468 )
469 .await,
470 )
471 .await
472}
473
474#[derive(Default, Clone, Debug)]
476pub struct ClaimQueueSnapshot(pub BTreeMap<CoreIndex, VecDeque<ParaId>>);
477
478impl From<BTreeMap<CoreIndex, VecDeque<ParaId>>> for ClaimQueueSnapshot {
479 fn from(claim_queue_snapshot: BTreeMap<CoreIndex, VecDeque<ParaId>>) -> Self {
480 ClaimQueueSnapshot(claim_queue_snapshot)
481 }
482}
483
484impl ClaimQueueSnapshot {
485 pub fn get_claim_for(&self, core_index: CoreIndex, depth: usize) -> Option<ParaId> {
488 self.0.get(&core_index)?.get(depth).copied()
489 }
490
491 pub fn iter_claims_at_depth(
494 &self,
495 depth: usize,
496 ) -> impl Iterator<Item = (CoreIndex, ParaId)> + '_ {
497 self.0
498 .iter()
499 .filter_map(move |(core_index, paras)| Some((*core_index, *paras.get(depth)?)))
500 }
501
502 pub fn iter_claims_for_core(
504 &self,
505 core_index: &CoreIndex,
506 ) -> impl Iterator<Item = &ParaId> + '_ {
507 self.0.get(core_index).map(|c| c.iter()).into_iter().flatten()
508 }
509
510 pub fn iter_all_claims(&self) -> impl Iterator<Item = (&CoreIndex, &VecDeque<ParaId>)> + '_ {
512 self.0.iter()
513 }
514
515 pub fn iter_claims_at_depth_for_para(
517 &self,
518 depth: usize,
519 para_id: ParaId,
520 ) -> impl Iterator<Item = CoreIndex> + '_ {
521 self.0.iter().filter_map(move |(core_index, ids)| {
522 ids.get(depth).filter(|id| **id == para_id).map(|_| *core_index)
523 })
524 }
525}
526
527pub async fn fetch_claim_queue(
529 sender: &mut impl SubsystemSender<RuntimeApiMessage>,
530 relay_parent: Hash,
531) -> Result<ClaimQueueSnapshot> {
532 let cq = request_claim_queue(relay_parent, sender)
533 .await
534 .await
535 .map_err(Error::RuntimeRequestCanceled)??;
536
537 Ok(cq.into())
538}
539
540pub async fn fetch_scheduling_lookahead(
543 parent: Hash,
544 session_index: SessionIndex,
545 sender: &mut impl overseer::SubsystemSender<RuntimeApiMessage>,
546) -> Result<u32> {
547 let res = recv_runtime(
548 request_from_runtime(parent, sender, |tx| {
549 RuntimeApiRequest::SchedulingLookahead(session_index, tx)
550 })
551 .await,
552 )
553 .await;
554
555 if let Err(Error::RuntimeRequest(RuntimeApiError::NotSupported { .. })) = res {
556 gum::trace!(
557 target: LOG_TARGET,
558 ?parent,
559 "Querying the scheduling lookahead from the runtime is not supported by the current Runtime API, falling back to default value of {}",
560 DEFAULT_SCHEDULING_LOOKAHEAD
561 );
562
563 Ok(DEFAULT_SCHEDULING_LOOKAHEAD)
564 } else {
565 res
566 }
567}
568
569pub async fn fetch_validation_code_bomb_limit(
571 parent: Hash,
572 session_index: SessionIndex,
573 sender: &mut impl overseer::SubsystemSender<RuntimeApiMessage>,
574) -> Result<u32> {
575 let res = recv_runtime(
576 request_from_runtime(parent, sender, |tx| {
577 RuntimeApiRequest::ValidationCodeBombLimit(session_index, tx)
578 })
579 .await,
580 )
581 .await;
582
583 if let Err(Error::RuntimeRequest(RuntimeApiError::NotSupported { .. })) = res {
584 gum::trace!(
585 target: LOG_TARGET,
586 ?parent,
587 "Querying the validation code bomb limit from the runtime is not supported by the current Runtime API",
588 );
589
590 #[allow(deprecated)]
592 Ok(polkadot_node_primitives::VALIDATION_CODE_BOMB_LIMIT as u32)
593 } else {
594 res
595 }
596}
597
598#[cfg(test)]
599mod test {
600 use super::*;
601
602 #[test]
603 fn iter_claims_at_depth_for_para_works() {
604 let claim_queue = ClaimQueueSnapshot(BTreeMap::from_iter(
605 [
606 (
607 CoreIndex(0),
608 VecDeque::from_iter([ParaId::from(1), ParaId::from(2), ParaId::from(1)]),
609 ),
610 (
611 CoreIndex(1),
612 VecDeque::from_iter([ParaId::from(1), ParaId::from(1), ParaId::from(2)]),
613 ),
614 (
615 CoreIndex(2),
616 VecDeque::from_iter([ParaId::from(1), ParaId::from(2), ParaId::from(3)]),
617 ),
618 (
619 CoreIndex(3),
620 VecDeque::from_iter([ParaId::from(2), ParaId::from(1), ParaId::from(3)]),
621 ),
622 ]
623 .into_iter(),
624 ));
625
626 let depth_0_cores =
628 claim_queue.iter_claims_at_depth_for_para(0, 1u32.into()).collect::<Vec<_>>();
629 assert_eq!(depth_0_cores.len(), 3);
630 assert_eq!(depth_0_cores, vec![CoreIndex(0), CoreIndex(1), CoreIndex(2)]);
631
632 let depth_1_cores =
634 claim_queue.iter_claims_at_depth_for_para(1, 1u32.into()).collect::<Vec<_>>();
635 assert_eq!(depth_1_cores.len(), 2);
636 assert_eq!(depth_1_cores, vec![CoreIndex(1), CoreIndex(3)]);
637
638 let depth_2_cores =
640 claim_queue.iter_claims_at_depth_for_para(2, 1u32.into()).collect::<Vec<_>>();
641 assert_eq!(depth_2_cores.len(), 1);
642 assert_eq!(depth_2_cores, vec![CoreIndex(0)]);
643
644 let depth_3_cores =
646 claim_queue.iter_claims_at_depth_for_para(3, 1u32.into()).collect::<Vec<_>>();
647 assert!(depth_3_cores.is_empty());
648
649 let depth_0_cores =
651 claim_queue.iter_claims_at_depth_for_para(0, 2u32.into()).collect::<Vec<_>>();
652 assert_eq!(depth_0_cores.len(), 1);
653 assert_eq!(depth_0_cores, vec![CoreIndex(3)]);
654
655 let depth_1_cores =
657 claim_queue.iter_claims_at_depth_for_para(1, 2u32.into()).collect::<Vec<_>>();
658 assert_eq!(depth_1_cores.len(), 2);
659 assert_eq!(depth_1_cores, vec![CoreIndex(0), CoreIndex(2)]);
660
661 let depth_2_cores =
663 claim_queue.iter_claims_at_depth_for_para(2, 2u32.into()).collect::<Vec<_>>();
664 assert_eq!(depth_2_cores.len(), 1);
665 assert_eq!(depth_2_cores, vec![CoreIndex(1)]);
666
667 let depth_0_cores =
669 claim_queue.iter_claims_at_depth_for_para(0, 3u32.into()).collect::<Vec<_>>();
670 assert!(depth_0_cores.is_empty());
671
672 let depth_1_cores =
674 claim_queue.iter_claims_at_depth_for_para(1, 3u32.into()).collect::<Vec<_>>();
675 assert!(depth_1_cores.is_empty());
676
677 let depth_2_cores =
679 claim_queue.iter_claims_at_depth_for_para(2, 3u32.into()).collect::<Vec<_>>();
680 assert_eq!(depth_2_cores.len(), 2);
681 assert_eq!(depth_2_cores, vec![CoreIndex(2), CoreIndex(3)]);
682
683 let depth_0_cores =
685 claim_queue.iter_claims_at_depth_for_para(0, 99u32.into()).collect::<Vec<_>>();
686 assert!(depth_0_cores.is_empty());
687 }
688}