polkadot_node_subsystem_util/runtime/
mod.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// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Convenient interface to runtime information.
18
19use 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_unapplied_slashes_v2, request_validation_code_by_hash,
49	request_validator_groups,
50};
51
52/// Errors that can happen on runtime fetches.
53mod error;
54
55use error::Result;
56pub use error::{recv_runtime, Error, FatalError, JfyiError};
57
58const LOG_TARGET: &'static str = "parachain::runtime-info";
59
60/// Configuration for construction a `RuntimeInfo`.
61pub struct Config {
62	/// Needed for retrieval of `ValidatorInfo`
63	///
64	/// Pass `None` if you are not interested.
65	pub keystore: Option<KeystorePtr>,
66
67	/// How many sessions should we keep in the cache?
68	pub session_cache_lru_size: u32,
69}
70
71/// Caching of session info.
72///
73/// It should be ensured that a cached session stays live in the cache as long as we might need it.
74pub struct RuntimeInfo {
75	/// Get the session index for a given relay parent.
76	///
77	/// We query this up to a 100 times per block, so caching it here without roundtrips over the
78	/// overseer seems sensible.
79	session_index_cache: LruMap<Hash, SessionIndex>,
80
81	/// In the happy case, we do not query disabled validators at all. In the worst case, we can
82	/// query it order of `n_cores` times `n_validators` per block, so caching it here seems
83	/// sensible.
84	disabled_validators_cache: LruMap<Hash, Vec<ValidatorIndex>>,
85
86	/// Look up cached sessions by `SessionIndex`.
87	session_info_cache: LruMap<SessionIndex, ExtendedSessionInfo>,
88
89	/// Unpin handle of *some* block in the session.
90	/// Only blocks pinned explicitly by `pin_block` are stored here.
91	pinned_blocks: LruMap<SessionIndex, UnpinHandle>,
92
93	/// Key store for determining whether we are a validator and what `ValidatorIndex` we have.
94	keystore: Option<KeystorePtr>,
95}
96
97/// `SessionInfo` with additional useful data for validator nodes.
98pub struct ExtendedSessionInfo {
99	/// Actual session info as fetched from the runtime.
100	pub session_info: SessionInfo,
101	/// Contains useful information about ourselves, in case this node is a validator.
102	pub validator_info: ValidatorInfo,
103	/// Session executor parameters
104	pub executor_params: ExecutorParams,
105	/// Node features
106	pub node_features: NodeFeatures,
107}
108
109/// Information about ourselves, in case we are an `Authority`.
110///
111/// This data is derived from the `SessionInfo` and our key as found in the keystore.
112pub struct ValidatorInfo {
113	/// The index this very validator has in `SessionInfo` vectors, if any.
114	pub our_index: Option<ValidatorIndex>,
115	/// The group we belong to, if any.
116	pub our_group: Option<GroupIndex>,
117}
118
119impl Default for Config {
120	fn default() -> Self {
121		Self {
122			keystore: None,
123			// Usually we need to cache the current and the last session.
124			session_cache_lru_size: 2,
125		}
126	}
127}
128
129impl RuntimeInfo {
130	/// Create a new `RuntimeInfo` for convenient runtime fetches.
131	pub fn new(keystore: Option<KeystorePtr>) -> Self {
132		Self::new_with_config(Config { keystore, ..Default::default() })
133	}
134
135	/// Create with more elaborate configuration options.
136	pub fn new_with_config(cfg: Config) -> Self {
137		Self {
138			// Usually messages are processed for blocks pointing to hashes from last finalized
139			// block to to best, so make this cache large enough to hold at least this amount of
140			// hashes, so that we get the benefit of caching even when finality lag is large.
141			session_index_cache: LruMap::new(ByLength::new(
142				cfg.session_cache_lru_size.max(2 * MAX_FINALITY_LAG),
143			)),
144			session_info_cache: LruMap::new(ByLength::new(cfg.session_cache_lru_size)),
145			disabled_validators_cache: LruMap::new(ByLength::new(100)),
146			pinned_blocks: LruMap::new(ByLength::new(cfg.session_cache_lru_size)),
147			keystore: cfg.keystore,
148		}
149	}
150
151	/// Returns the session index expected at any child of the `parent` block.
152	/// This does not return the session index for the `parent` block.
153	pub async fn get_session_index_for_child<Sender>(
154		&mut self,
155		sender: &mut Sender,
156		parent: Hash,
157	) -> Result<SessionIndex>
158	where
159		Sender: SubsystemSender<RuntimeApiMessage>,
160	{
161		match self.session_index_cache.get(&parent) {
162			Some(index) => Ok(*index),
163			None => {
164				let index =
165					recv_runtime(request_session_index_for_child(parent, sender).await).await?;
166				self.session_index_cache.insert(parent, index);
167				Ok(index)
168			},
169		}
170	}
171
172	/// Pin a given block in the given session if none are pinned in that session.
173	/// Unpinning will happen automatically when LRU cache grows over the limit.
174	pub fn pin_block(&mut self, session_index: SessionIndex, unpin_handle: UnpinHandle) {
175		self.pinned_blocks.get_or_insert(session_index, || unpin_handle);
176	}
177
178	/// Get the hash of a pinned block for the given session index, if any.
179	pub fn get_block_in_session(&self, session_index: SessionIndex) -> Option<Hash> {
180		self.pinned_blocks.peek(&session_index).map(|h| h.hash())
181	}
182
183	/// Get `ExtendedSessionInfo` by relay parent hash.
184	pub async fn get_session_info<'a, Sender>(
185		&'a mut self,
186		sender: &mut Sender,
187		relay_parent: Hash,
188	) -> Result<&'a ExtendedSessionInfo>
189	where
190		Sender: SubsystemSender<RuntimeApiMessage>,
191	{
192		let session_index = self.get_session_index_for_child(sender, relay_parent).await?;
193
194		self.get_session_info_by_index(sender, relay_parent, session_index).await
195	}
196
197	/// Get the list of disabled validators at the relay parent.
198	pub async fn get_disabled_validators<Sender>(
199		&mut self,
200		sender: &mut Sender,
201		relay_parent: Hash,
202	) -> Result<Vec<ValidatorIndex>>
203	where
204		Sender: SubsystemSender<RuntimeApiMessage>,
205	{
206		match self.disabled_validators_cache.get(&relay_parent).cloned() {
207			Some(result) => Ok(result),
208			None => {
209				let disabled_validators =
210					request_disabled_validators(relay_parent, sender).await.await??;
211				self.disabled_validators_cache.insert(relay_parent, disabled_validators.clone());
212				Ok(disabled_validators)
213			},
214		}
215	}
216
217	/// Get `ExtendedSessionInfo` by session index.
218	///
219	/// `request_session_info` still requires the parent to be passed in, so we take the parent
220	/// in addition to the `SessionIndex`.
221	pub async fn get_session_info_by_index<'a, Sender>(
222		&'a mut self,
223		sender: &mut Sender,
224		parent: Hash,
225		session_index: SessionIndex,
226	) -> Result<&'a ExtendedSessionInfo>
227	where
228		Sender: SubsystemSender<RuntimeApiMessage>,
229	{
230		if self.session_info_cache.get(&session_index).is_none() {
231			let session_info =
232				recv_runtime(request_session_info(parent, session_index, sender).await)
233					.await?
234					.ok_or(JfyiError::NoSuchSession(session_index))?;
235
236			let executor_params =
237				recv_runtime(request_session_executor_params(parent, session_index, sender).await)
238					.await?
239					.ok_or(JfyiError::NoExecutorParams(session_index))?;
240
241			let validator_info = self.get_validator_info(&session_info)?;
242
243			let node_features =
244				request_node_features(parent, session_index, sender).await.await??;
245			let last_set_index = node_features.iter_ones().last().unwrap_or_default();
246			if last_set_index >= FeatureIndex::FirstUnassigned as usize {
247				gum::warn!(target: LOG_TARGET, "Runtime requires feature bit {} that node doesn't support, please upgrade node version", last_set_index);
248			}
249
250			let full_info = ExtendedSessionInfo {
251				session_info,
252				validator_info,
253				executor_params,
254				node_features,
255			};
256
257			self.session_info_cache.insert(session_index, full_info);
258		}
259		Ok(self
260			.session_info_cache
261			.get(&session_index)
262			.expect("We just put the value there. qed."))
263	}
264
265	/// Convenience function for checking the signature of something signed.
266	pub async fn check_signature<Sender, Payload, RealPayload>(
267		&mut self,
268		sender: &mut Sender,
269		relay_parent: Hash,
270		signed: UncheckedSigned<Payload, RealPayload>,
271	) -> Result<
272		std::result::Result<Signed<Payload, RealPayload>, UncheckedSigned<Payload, RealPayload>>,
273	>
274	where
275		Sender: SubsystemSender<RuntimeApiMessage>,
276		Payload: EncodeAs<RealPayload> + Clone,
277		RealPayload: Encode + Clone,
278	{
279		let session_index = self.get_session_index_for_child(sender, relay_parent).await?;
280		let info = self.get_session_info_by_index(sender, relay_parent, session_index).await?;
281		Ok(check_signature(session_index, &info.session_info, relay_parent, signed))
282	}
283
284	/// Build `ValidatorInfo` for the current session.
285	///
286	///
287	/// Returns: `None` if not a parachain validator.
288	fn get_validator_info(&self, session_info: &SessionInfo) -> Result<ValidatorInfo> {
289		if let Some(our_index) = self.get_our_index(&session_info.validators) {
290			// Get our group index:
291			let our_group =
292				session_info.validator_groups.iter().enumerate().find_map(|(i, g)| {
293					g.iter().find_map(|v| {
294						if *v == our_index {
295							Some(GroupIndex(i as u32))
296						} else {
297							None
298						}
299					})
300				});
301			let info = ValidatorInfo { our_index: Some(our_index), our_group };
302			return Ok(info)
303		}
304		return Ok(ValidatorInfo { our_index: None, our_group: None })
305	}
306
307	/// Get our `ValidatorIndex`.
308	///
309	/// Returns: None if we are not a validator.
310	fn get_our_index(
311		&self,
312		validators: &IndexedVec<ValidatorIndex, ValidatorId>,
313	) -> Option<ValidatorIndex> {
314		let keystore = self.keystore.as_ref()?;
315		for (i, v) in validators.iter().enumerate() {
316			if Keystore::has_keys(&**keystore, &[(v.to_raw_vec(), ValidatorId::ID)]) {
317				return Some(ValidatorIndex(i as u32))
318			}
319		}
320		None
321	}
322}
323
324/// Convenience function for quickly checking the signature on signed data.
325pub fn check_signature<Payload, RealPayload>(
326	session_index: SessionIndex,
327	session_info: &SessionInfo,
328	relay_parent: Hash,
329	signed: UncheckedSigned<Payload, RealPayload>,
330) -> std::result::Result<Signed<Payload, RealPayload>, UncheckedSigned<Payload, RealPayload>>
331where
332	Payload: EncodeAs<RealPayload> + Clone,
333	RealPayload: Encode + Clone,
334{
335	let signing_context = SigningContext { session_index, parent_hash: relay_parent };
336
337	session_info
338		.validators
339		.get(signed.unchecked_validator_index())
340		.ok_or_else(|| signed.clone())
341		.and_then(|v| signed.try_into_checked(&signing_context, v))
342}
343
344/// Request availability cores from the runtime.
345pub async fn get_availability_cores<Sender>(
346	sender: &mut Sender,
347	relay_parent: Hash,
348) -> Result<Vec<CoreState>>
349where
350	Sender: overseer::SubsystemSender<RuntimeApiMessage>,
351{
352	recv_runtime(request_availability_cores(relay_parent, sender).await).await
353}
354
355/// Variant of `request_availability_cores` that only returns occupied ones.
356pub async fn get_occupied_cores<Sender>(
357	sender: &mut Sender,
358	relay_parent: Hash,
359) -> Result<Vec<(CoreIndex, OccupiedCore)>>
360where
361	Sender: overseer::SubsystemSender<RuntimeApiMessage>,
362{
363	let cores = get_availability_cores(sender, relay_parent).await?;
364
365	Ok(cores
366		.into_iter()
367		.enumerate()
368		.filter_map(|(core_index, core_state)| {
369			if let CoreState::Occupied(occupied) = core_state {
370				Some((CoreIndex(core_index as u32), occupied))
371			} else {
372				None
373			}
374		})
375		.collect())
376}
377
378/// Get group rotation info based on the given `relay_parent`.
379pub async fn get_group_rotation_info<Sender>(
380	sender: &mut Sender,
381	relay_parent: Hash,
382) -> Result<GroupRotationInfo>
383where
384	Sender: overseer::SubsystemSender<RuntimeApiMessage>,
385{
386	// We drop `groups` here as we don't need them, because of `RuntimeInfo`. Ideally we would not
387	// fetch them in the first place.
388	let (_, info) = recv_runtime(request_validator_groups(relay_parent, sender).await).await?;
389	Ok(info)
390}
391
392/// Get `CandidateEvent`s for the given `relay_parent`.
393pub async fn get_candidate_events<Sender>(
394	sender: &mut Sender,
395	relay_parent: Hash,
396) -> Result<Vec<CandidateEvent>>
397where
398	Sender: SubsystemSender<RuntimeApiMessage>,
399{
400	recv_runtime(request_candidate_events(relay_parent, sender).await).await
401}
402
403/// Get on chain votes.
404pub async fn get_on_chain_votes<Sender>(
405	sender: &mut Sender,
406	relay_parent: Hash,
407) -> Result<Option<ScrapedOnChainVotes>>
408where
409	Sender: SubsystemSender<RuntimeApiMessage>,
410{
411	recv_runtime(request_on_chain_votes(relay_parent, sender).await).await
412}
413
414/// Fetch `ValidationCode` by hash from the runtime.
415pub async fn get_validation_code_by_hash<Sender>(
416	sender: &mut Sender,
417	relay_parent: Hash,
418	validation_code_hash: ValidationCodeHash,
419) -> Result<Option<ValidationCode>>
420where
421	Sender: SubsystemSender<RuntimeApiMessage>,
422{
423	recv_runtime(request_validation_code_by_hash(relay_parent, validation_code_hash, sender).await)
424		.await
425}
426
427/// Fetch a list of `PendingSlashes` from the runtime.
428/// Will fallback to `unapplied_slashes` if the runtime does not
429/// support `unapplied_slashes_v2`.
430pub async fn get_unapplied_slashes<Sender>(
431	sender: &mut Sender,
432	relay_parent: Hash,
433) -> Result<Vec<(SessionIndex, CandidateHash, slashing::PendingSlashes)>>
434where
435	Sender: SubsystemSender<RuntimeApiMessage>,
436{
437	match recv_runtime(request_unapplied_slashes_v2(relay_parent, sender).await).await {
438		Ok(v2) => Ok(v2),
439		Err(Error::RuntimeRequest(RuntimeApiError::NotSupported { .. })) => {
440			// Fallback to legacy unapplied_slashes
441			let legacy =
442				recv_runtime(request_unapplied_slashes(relay_parent, sender).await).await?;
443			// Convert legacy slashes to PendingSlashes
444			Ok(legacy
445				.into_iter()
446				.map(|(session, candidate_hash, legacy_slash)| {
447					(
448						session,
449						candidate_hash,
450						slashing::PendingSlashes {
451							keys: legacy_slash.keys,
452							kind: legacy_slash.kind.into(),
453						},
454					)
455				})
456				.collect())
457		},
458		Err(e) => Err(e),
459	}
460}
461
462/// Generate validator key ownership proof.
463///
464/// Note: The choice of `relay_parent` is important here, it needs to match
465/// the desired session index of the validator set in question.
466pub async fn key_ownership_proof<Sender>(
467	sender: &mut Sender,
468	relay_parent: Hash,
469	validator_id: ValidatorId,
470) -> Result<Option<slashing::OpaqueKeyOwnershipProof>>
471where
472	Sender: SubsystemSender<RuntimeApiMessage>,
473{
474	recv_runtime(request_key_ownership_proof(relay_parent, validator_id, sender).await).await
475}
476
477/// Submit a past-session dispute slashing report.
478pub async fn submit_report_dispute_lost<Sender>(
479	sender: &mut Sender,
480	relay_parent: Hash,
481	dispute_proof: slashing::DisputeProof,
482	key_ownership_proof: slashing::OpaqueKeyOwnershipProof,
483) -> Result<Option<()>>
484where
485	Sender: SubsystemSender<RuntimeApiMessage>,
486{
487	recv_runtime(
488		request_submit_report_dispute_lost(
489			relay_parent,
490			dispute_proof,
491			key_ownership_proof,
492			sender,
493		)
494		.await,
495	)
496	.await
497}
498
499/// A snapshot of the runtime claim queue at an arbitrary relay chain block.
500#[derive(Default, Clone, Debug)]
501pub struct ClaimQueueSnapshot(pub BTreeMap<CoreIndex, VecDeque<ParaId>>);
502
503impl From<BTreeMap<CoreIndex, VecDeque<ParaId>>> for ClaimQueueSnapshot {
504	fn from(claim_queue_snapshot: BTreeMap<CoreIndex, VecDeque<ParaId>>) -> Self {
505		ClaimQueueSnapshot(claim_queue_snapshot)
506	}
507}
508
509impl ClaimQueueSnapshot {
510	/// Returns the `ParaId` that has a claim for `core_index` at the specified `depth` in the
511	/// claim queue. A depth of `0` means the very next block.
512	pub fn get_claim_for(&self, core_index: CoreIndex, depth: usize) -> Option<ParaId> {
513		self.0.get(&core_index)?.get(depth).copied()
514	}
515
516	/// Returns an iterator over all claimed cores and the claiming `ParaId` at the specified
517	/// `depth` in the claim queue.
518	pub fn iter_claims_at_depth(
519		&self,
520		depth: usize,
521	) -> impl Iterator<Item = (CoreIndex, ParaId)> + '_ {
522		self.0
523			.iter()
524			.filter_map(move |(core_index, paras)| Some((*core_index, *paras.get(depth)?)))
525	}
526
527	/// Returns an iterator over all claims on the given core.
528	pub fn iter_claims_for_core(
529		&self,
530		core_index: &CoreIndex,
531	) -> impl Iterator<Item = &ParaId> + '_ {
532		self.0.get(core_index).map(|c| c.iter()).into_iter().flatten()
533	}
534
535	/// Returns an iterator over the whole claim queue.
536	pub fn iter_all_claims(&self) -> impl Iterator<Item = (&CoreIndex, &VecDeque<ParaId>)> + '_ {
537		self.0.iter()
538	}
539
540	/// Get all claimed cores for the given `para_id` at the specified depth.
541	pub fn iter_claims_at_depth_for_para(
542		&self,
543		depth: usize,
544		para_id: ParaId,
545	) -> impl Iterator<Item = CoreIndex> + '_ {
546		self.0.iter().filter_map(move |(core_index, ids)| {
547			ids.get(depth).filter(|id| **id == para_id).map(|_| *core_index)
548		})
549	}
550}
551
552/// Fetch the claim queue and wrap it into a helpful `ClaimQueueSnapshot`
553pub async fn fetch_claim_queue(
554	sender: &mut impl SubsystemSender<RuntimeApiMessage>,
555	relay_parent: Hash,
556) -> Result<ClaimQueueSnapshot> {
557	let cq = request_claim_queue(relay_parent, sender)
558		.await
559		.await
560		.map_err(Error::RuntimeRequestCanceled)??;
561
562	Ok(cq.into())
563}
564
565/// Returns the lookahead from the scheduler params if the runtime supports it,
566/// or default value if scheduling lookahead API is not supported by runtime.
567pub async fn fetch_scheduling_lookahead(
568	parent: Hash,
569	session_index: SessionIndex,
570	sender: &mut impl overseer::SubsystemSender<RuntimeApiMessage>,
571) -> Result<u32> {
572	let res = recv_runtime(
573		request_from_runtime(parent, sender, |tx| {
574			RuntimeApiRequest::SchedulingLookahead(session_index, tx)
575		})
576		.await,
577	)
578	.await;
579
580	if let Err(Error::RuntimeRequest(RuntimeApiError::NotSupported { .. })) = res {
581		gum::trace!(
582			target: LOG_TARGET,
583			?parent,
584			"Querying the scheduling lookahead from the runtime is not supported by the current Runtime API, falling back to default value of {}",
585			DEFAULT_SCHEDULING_LOOKAHEAD
586		);
587
588		Ok(DEFAULT_SCHEDULING_LOOKAHEAD)
589	} else {
590		res
591	}
592}
593
594/// Fetch the validation code bomb limit from the runtime.
595pub async fn fetch_validation_code_bomb_limit(
596	parent: Hash,
597	session_index: SessionIndex,
598	sender: &mut impl overseer::SubsystemSender<RuntimeApiMessage>,
599) -> Result<u32> {
600	let res = recv_runtime(
601		request_from_runtime(parent, sender, |tx| {
602			RuntimeApiRequest::ValidationCodeBombLimit(session_index, tx)
603		})
604		.await,
605	)
606	.await;
607
608	if let Err(Error::RuntimeRequest(RuntimeApiError::NotSupported { .. })) = res {
609		gum::trace!(
610			target: LOG_TARGET,
611			?parent,
612			"Querying the validation code bomb limit from the runtime is not supported by the current Runtime API",
613		);
614
615		// TODO: Remove this once runtime API version 12 is released.
616		#[allow(deprecated)]
617		Ok(polkadot_node_primitives::VALIDATION_CODE_BOMB_LIMIT as u32)
618	} else {
619		res
620	}
621}
622
623#[cfg(test)]
624mod test {
625	use super::*;
626
627	#[test]
628	fn iter_claims_at_depth_for_para_works() {
629		let claim_queue = ClaimQueueSnapshot(BTreeMap::from_iter(
630			[
631				(
632					CoreIndex(0),
633					VecDeque::from_iter([ParaId::from(1), ParaId::from(2), ParaId::from(1)]),
634				),
635				(
636					CoreIndex(1),
637					VecDeque::from_iter([ParaId::from(1), ParaId::from(1), ParaId::from(2)]),
638				),
639				(
640					CoreIndex(2),
641					VecDeque::from_iter([ParaId::from(1), ParaId::from(2), ParaId::from(3)]),
642				),
643				(
644					CoreIndex(3),
645					VecDeque::from_iter([ParaId::from(2), ParaId::from(1), ParaId::from(3)]),
646				),
647			]
648			.into_iter(),
649		));
650
651		// Test getting claims for para_id 1 at depth 0: cores 0, 1, 2
652		let depth_0_cores =
653			claim_queue.iter_claims_at_depth_for_para(0, 1u32.into()).collect::<Vec<_>>();
654		assert_eq!(depth_0_cores.len(), 3);
655		assert_eq!(depth_0_cores, vec![CoreIndex(0), CoreIndex(1), CoreIndex(2)]);
656
657		// Test getting claims for para_id 1 at depth 1: cores 1, 3
658		let depth_1_cores =
659			claim_queue.iter_claims_at_depth_for_para(1, 1u32.into()).collect::<Vec<_>>();
660		assert_eq!(depth_1_cores.len(), 2);
661		assert_eq!(depth_1_cores, vec![CoreIndex(1), CoreIndex(3)]);
662
663		// Test getting claims for para_id 1 at depth 2: core 0
664		let depth_2_cores =
665			claim_queue.iter_claims_at_depth_for_para(2, 1u32.into()).collect::<Vec<_>>();
666		assert_eq!(depth_2_cores.len(), 1);
667		assert_eq!(depth_2_cores, vec![CoreIndex(0)]);
668
669		// Test getting claims for para_id 1 at depth 3: no claims
670		let depth_3_cores =
671			claim_queue.iter_claims_at_depth_for_para(3, 1u32.into()).collect::<Vec<_>>();
672		assert!(depth_3_cores.is_empty());
673
674		// Test getting claims for para_id 2 at depth 0: core 3
675		let depth_0_cores =
676			claim_queue.iter_claims_at_depth_for_para(0, 2u32.into()).collect::<Vec<_>>();
677		assert_eq!(depth_0_cores.len(), 1);
678		assert_eq!(depth_0_cores, vec![CoreIndex(3)]);
679
680		// Test getting claims for para_id 2 at depth 1: cores 0, 2
681		let depth_1_cores =
682			claim_queue.iter_claims_at_depth_for_para(1, 2u32.into()).collect::<Vec<_>>();
683		assert_eq!(depth_1_cores.len(), 2);
684		assert_eq!(depth_1_cores, vec![CoreIndex(0), CoreIndex(2)]);
685
686		// Test getting claims for para_id 2 at depth 2: core 1
687		let depth_2_cores =
688			claim_queue.iter_claims_at_depth_for_para(2, 2u32.into()).collect::<Vec<_>>();
689		assert_eq!(depth_2_cores.len(), 1);
690		assert_eq!(depth_2_cores, vec![CoreIndex(1)]);
691
692		// Test getting claims for para_id 3 at depth 0: no claims
693		let depth_0_cores =
694			claim_queue.iter_claims_at_depth_for_para(0, 3u32.into()).collect::<Vec<_>>();
695		assert!(depth_0_cores.is_empty());
696
697		// Test getting claims for para_id 3 at depth 1: no claims
698		let depth_1_cores =
699			claim_queue.iter_claims_at_depth_for_para(1, 3u32.into()).collect::<Vec<_>>();
700		assert!(depth_1_cores.is_empty());
701
702		// Test getting claims for para_id 3 at depth 2: cores 2, 3
703		let depth_2_cores =
704			claim_queue.iter_claims_at_depth_for_para(2, 3u32.into()).collect::<Vec<_>>();
705		assert_eq!(depth_2_cores.len(), 2);
706		assert_eq!(depth_2_cores, vec![CoreIndex(2), CoreIndex(3)]);
707
708		// Test getting claims for non-existent para_id at depth 0: no claims
709		let depth_0_cores =
710			claim_queue.iter_claims_at_depth_for_para(0, 99u32.into()).collect::<Vec<_>>();
711		assert!(depth_0_cores.is_empty());
712	}
713}