referrerpolicy=no-referrer-when-downgrade

polkadot_node_subsystem_util/
backing_implicit_view.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
17use futures::channel::oneshot;
18use polkadot_node_subsystem::{
19	errors::ChainApiError,
20	messages::{ChainApiMessage, ProspectiveParachainsMessage, RuntimeApiMessage},
21	SubsystemSender,
22};
23use polkadot_primitives::{BlockNumber, Hash, Id as ParaId};
24
25use std::collections::{HashMap, HashSet};
26
27use crate::{
28	inclusion_emulator::RelayChainBlockInfo,
29	request_session_index_for_child,
30	runtime::{self, fetch_scheduling_lookahead, recv_runtime},
31	LOG_TARGET,
32};
33
34// Always aim to retain 1 block before the active leaves.
35const MINIMUM_RETAIN_LENGTH: BlockNumber = 2;
36
37/// Handles the implicit view of the relay chain derived from the immediate view, which
38/// is composed of active leaves, and the minimum relay-parents allowed for
39/// candidates of various parachains at those leaves.
40#[derive(Clone)]
41pub struct View {
42	leaves: HashMap<Hash, ActiveLeafPruningInfo>,
43	block_info_storage: HashMap<Hash, BlockInfo>,
44	collating_for: Option<ParaId>,
45}
46
47impl View {
48	/// Create a new empty view.
49	/// If `collating_for` is `Some`, the node is a collator and is only interested in the allowed
50	/// relay parents of a single paraid. When this is true, prospective-parachains is no longer
51	/// queried.
52	pub fn new(collating_for: Option<ParaId>) -> Self {
53		Self { leaves: Default::default(), block_info_storage: Default::default(), collating_for }
54	}
55}
56
57impl Default for View {
58	fn default() -> Self {
59		Self::new(None)
60	}
61}
62
63// Minimum relay parents implicitly relative to a particular block.
64#[derive(Debug, Clone)]
65struct AllowedRelayParents {
66	// minimum relay parents can only be fetched for active leaves,
67	// so this will be empty for all blocks that haven't ever been
68	// witnessed as active leaves.
69	minimum_relay_parents: HashMap<ParaId, BlockNumber>,
70	// Ancestry, in descending order, starting from the block hash itself down
71	// to and including the minimum of `minimum_relay_parents`.
72	allowed_relay_parents_contiguous: Vec<Hash>,
73}
74
75impl AllowedRelayParents {
76	fn allowed_relay_parents_for(
77		&self,
78		para_id: Option<ParaId>,
79		base_number: BlockNumber,
80	) -> &[Hash] {
81		let para_id = match para_id {
82			None => return &self.allowed_relay_parents_contiguous[..],
83			Some(p) => p,
84		};
85
86		let para_min = match self.minimum_relay_parents.get(&para_id) {
87			Some(p) => *p,
88			None => return &[],
89		};
90
91		if base_number < para_min {
92			return &[]
93		}
94
95		let diff = base_number - para_min;
96
97		// difference of 0 should lead to slice len of 1
98		let slice_len = ((diff + 1) as usize).min(self.allowed_relay_parents_contiguous.len());
99		&self.allowed_relay_parents_contiguous[..slice_len]
100	}
101}
102
103#[derive(Debug, Clone)]
104struct ActiveLeafPruningInfo {
105	// The minimum block in the same branch of the relay-chain that should be
106	// preserved.
107	retain_minimum: BlockNumber,
108}
109
110#[derive(Debug, Clone)]
111struct BlockInfo {
112	block_number: BlockNumber,
113	// If this was previously an active leaf, this will be `Some`
114	// and is useful for understanding the views of peers in the network
115	// which may not be in perfect synchrony with our own view.
116	//
117	// If they are ahead of us in getting a new leaf, there's nothing we
118	// can do as it's an unrecognized block hash. But if they're behind us,
119	// it's useful for us to retain some information about previous leaves'
120	// implicit views so we can continue to send relevant messages to them
121	// until they catch up.
122	maybe_allowed_relay_parents: Option<AllowedRelayParents>,
123	parent_hash: Hash,
124}
125
126/// Information about a relay-chain block, to be used when calling this module from prospective
127/// parachains.
128#[derive(Debug, Clone, PartialEq)]
129pub struct BlockInfoProspectiveParachains {
130	/// The hash of the relay-chain block.
131	pub hash: Hash,
132	/// The hash of the parent relay-chain block.
133	pub parent_hash: Hash,
134	/// The number of the relay-chain block.
135	pub number: BlockNumber,
136	/// The storage-root of the relay-chain block.
137	pub storage_root: Hash,
138}
139
140impl From<BlockInfoProspectiveParachains> for RelayChainBlockInfo {
141	fn from(value: BlockInfoProspectiveParachains) -> Self {
142		Self { hash: value.hash, number: value.number, storage_root: value.storage_root }
143	}
144}
145
146impl View {
147	/// Get an iterator over active leaves in the view.
148	pub fn leaves(&self) -> impl Iterator<Item = &Hash> {
149		self.leaves.keys()
150	}
151
152	/// Check if the given block hash is an active leaf of the current view.
153	pub fn contains_leaf(&self, leaf_hash: &Hash) -> bool {
154		self.leaves.contains_key(leaf_hash)
155	}
156
157	/// Get the block number of a leaf in the current view.
158	/// Returns `None` if leaf is not in the view.
159	pub fn block_number(&self, leaf_hash: &Hash) -> Option<BlockNumber> {
160		self.block_info_storage.get(leaf_hash).map(|block_info| block_info.block_number)
161	}
162
163	/// Activate a leaf in the view.
164	/// This will request the minimum relay parents the leaf and will load headers in the
165	/// ancestry of the leaf as needed. These are the 'implicit ancestors' of the leaf.
166	///
167	/// To maximize reuse of outdated leaves, it's best to activate new leaves before
168	/// deactivating old ones.
169	///
170	/// The allowed relay parents for the relevant paras under this leaf can be
171	/// queried with [`View::known_allowed_relay_parents_under`].
172	///
173	/// No-op for known leaves.
174	pub async fn activate_leaf<Sender>(
175		&mut self,
176		sender: &mut Sender,
177		leaf_hash: Hash,
178	) -> Result<(), FetchError>
179	where
180		Sender: SubsystemSender<ChainApiMessage>
181			+ SubsystemSender<ProspectiveParachainsMessage>
182			+ SubsystemSender<RuntimeApiMessage>,
183	{
184		if self.leaves.contains_key(&leaf_hash) {
185			return Err(FetchError::AlreadyKnown)
186		}
187
188		let res = self.fetch_fresh_leaf_and_insert_ancestry(leaf_hash, &mut *sender).await;
189
190		match res {
191			Ok(fetched) => {
192				// Retain at least `MINIMUM_RETAIN_LENGTH` blocks in storage.
193				// This helps to avoid Chain API calls when activating leaves in the
194				// same chain.
195				let retain_minimum = std::cmp::min(
196					fetched.minimum_ancestor_number,
197					fetched.leaf_number.saturating_sub(MINIMUM_RETAIN_LENGTH),
198				);
199
200				self.leaves.insert(leaf_hash, ActiveLeafPruningInfo { retain_minimum });
201
202				Ok(())
203			},
204			Err(e) => Err(e),
205		}
206	}
207
208	/// Activate a leaf in the view. To be used by the prospective parachains subsystem.
209	///
210	/// This will not request any additional data, as prospective parachains already provides all
211	/// the required info.
212	/// NOTE: using `activate_leaf` instead of this function will result in a
213	/// deadlock, as it calls prospective-parachains under the hood.
214	///
215	/// No-op for known leaves.
216	pub fn activate_leaf_from_prospective_parachains(
217		&mut self,
218		leaf: BlockInfoProspectiveParachains,
219		ancestors: &[BlockInfoProspectiveParachains],
220	) {
221		if self.leaves.contains_key(&leaf.hash) {
222			return
223		}
224
225		// Retain at least `MINIMUM_RETAIN_LENGTH` blocks in storage.
226		// This helps to avoid Chain API calls when activating leaves in the
227		// same chain.
228		let retain_minimum = std::cmp::min(
229			ancestors.last().map(|a| a.number).unwrap_or(0),
230			leaf.number.saturating_sub(MINIMUM_RETAIN_LENGTH),
231		);
232
233		self.leaves.insert(leaf.hash, ActiveLeafPruningInfo { retain_minimum });
234		let mut allowed_relay_parents = AllowedRelayParents {
235			allowed_relay_parents_contiguous: Vec::with_capacity(ancestors.len()),
236			// In this case, initialise this to an empty map, as prospective parachains already has
237			// this data and it won't query the implicit view for it.
238			minimum_relay_parents: HashMap::new(),
239		};
240
241		for ancestor in ancestors {
242			self.block_info_storage.insert(
243				ancestor.hash,
244				BlockInfo {
245					block_number: ancestor.number,
246					maybe_allowed_relay_parents: None,
247					parent_hash: ancestor.parent_hash,
248				},
249			);
250			allowed_relay_parents.allowed_relay_parents_contiguous.push(ancestor.hash);
251		}
252
253		self.block_info_storage.insert(
254			leaf.hash,
255			BlockInfo {
256				block_number: leaf.number,
257				maybe_allowed_relay_parents: Some(allowed_relay_parents),
258				parent_hash: leaf.parent_hash,
259			},
260		);
261	}
262
263	/// Deactivate a leaf in the view. This prunes any outdated implicit ancestors as well.
264	///
265	/// Returns hashes of blocks pruned from storage.
266	pub fn deactivate_leaf(&mut self, leaf_hash: Hash) -> Vec<Hash> {
267		let mut removed = Vec::new();
268
269		if self.leaves.remove(&leaf_hash).is_none() {
270			return removed
271		}
272
273		// Prune everything before the minimum out of all leaves,
274		// pruning absolutely everything if there are no leaves (empty view)
275		//
276		// Pruning by block number does leave behind orphaned forks slightly longer
277		// but the memory overhead is negligible.
278		{
279			let minimum = self.leaves.values().map(|l| l.retain_minimum).min();
280
281			self.block_info_storage.retain(|hash, i| {
282				let keep = minimum.map_or(false, |m| i.block_number >= m);
283				if !keep {
284					removed.push(*hash);
285				}
286				keep
287			});
288
289			removed
290		}
291	}
292
293	/// Get an iterator over all allowed relay-parents in the view with no particular order.
294	///
295	/// **Important**: not all blocks are guaranteed to be allowed for some leaves, it may
296	/// happen that a block info is only kept in the view storage because of a retaining rule.
297	///
298	/// For getting relay-parents that are valid for parachain candidates use
299	/// [`View::known_allowed_relay_parents_under`].
300	pub fn all_allowed_relay_parents(&self) -> impl Iterator<Item = &Hash> {
301		self.block_info_storage.keys()
302	}
303
304	/// Get the known, allowed relay-parents that are valid for parachain candidates
305	/// which could be backed in a child of a given block for a given para ID.
306	///
307	/// This is expressed as a contiguous slice of relay-chain block hashes which may
308	/// include the provided block hash itself.
309	///
310	/// If `para_id` is `None`, this returns all valid relay-parents across all paras
311	/// for the leaf.
312	///
313	/// `None` indicates that the block hash isn't part of the implicit view or that
314	/// there are no known allowed relay parents.
315	///
316	/// This always returns `Some` for active leaves or for blocks that previously
317	/// were active leaves.
318	///
319	/// This can return the empty slice, which indicates that no relay-parents are allowed
320	/// for the para, e.g. if the para is not scheduled at the given block hash.
321	pub fn known_allowed_relay_parents_under(
322		&self,
323		block_hash: &Hash,
324		para_id: Option<ParaId>,
325	) -> Option<&[Hash]> {
326		let block_info = self.block_info_storage.get(block_hash)?;
327		block_info
328			.maybe_allowed_relay_parents
329			.as_ref()
330			.map(|mins| mins.allowed_relay_parents_for(para_id, block_info.block_number))
331	}
332
333	/// Returns all paths from each leaf to the last block in state containing `relay_parent`. If no
334	/// paths exist the function will return an empty `Vec`.
335	pub fn paths_via_relay_parent(&self, relay_parent: &Hash) -> Vec<Vec<Hash>> {
336		gum::trace!(
337			target: LOG_TARGET,
338			?relay_parent,
339			leaves=?self.leaves,
340			block_info_storage=?self.block_info_storage,
341			"Finding paths via relay parent"
342		);
343
344		if self.leaves.is_empty() {
345			// No leaves so the view should be empty. Don't return any paths.
346			return vec![]
347		};
348
349		if !self.block_info_storage.contains_key(relay_parent) {
350			// `relay_parent` is not in the view - don't return any paths
351			return vec![]
352		}
353
354		// Find all paths from each leaf to `relay_parent`.
355		let mut paths = Vec::new();
356		for (leaf, _) in &self.leaves {
357			let mut path = Vec::new();
358			let mut current_leaf = *leaf;
359			let mut visited = HashSet::new();
360			let mut path_contains_target = false;
361
362			// Start from the leaf and traverse all known blocks
363			loop {
364				if visited.contains(&current_leaf) {
365					// There is a cycle - abandon this path
366					break
367				}
368
369				current_leaf = match self.block_info_storage.get(&current_leaf) {
370					Some(info) => {
371						// `current_leaf` is a known block - add it to the path and mark it as
372						// visited
373						path.push(current_leaf);
374						visited.insert(current_leaf);
375
376						// `current_leaf` is the target `relay_parent`. Mark the path so that it's
377						// included in the result
378						if current_leaf == *relay_parent {
379							path_contains_target = true;
380						}
381
382						// update `current_leaf` with the parent
383						info.parent_hash
384					},
385					None => {
386						// path is complete
387						if path_contains_target {
388							// we want the path ordered from oldest to newest so reverse it
389							paths.push(path.into_iter().rev().collect());
390						}
391						break
392					},
393				};
394			}
395		}
396
397		paths
398	}
399
400	async fn fetch_fresh_leaf_and_insert_ancestry<Sender>(
401		&mut self,
402		leaf_hash: Hash,
403		sender: &mut Sender,
404	) -> Result<FetchSummary, FetchError>
405	where
406		Sender: SubsystemSender<ChainApiMessage>
407			+ SubsystemSender<ProspectiveParachainsMessage>
408			+ SubsystemSender<RuntimeApiMessage>,
409	{
410		let leaf_header = {
411			let (tx, rx) = oneshot::channel();
412			sender.send_message(ChainApiMessage::BlockHeader(leaf_hash, tx)).await;
413
414			match rx.await {
415				Ok(Ok(Some(header))) => header,
416				Ok(Ok(None)) =>
417					return Err(FetchError::BlockHeaderUnavailable(
418						leaf_hash,
419						BlockHeaderUnavailableReason::Unknown,
420					)),
421				Ok(Err(e)) =>
422					return Err(FetchError::BlockHeaderUnavailable(
423						leaf_hash,
424						BlockHeaderUnavailableReason::Internal(e),
425					)),
426				Err(_) =>
427					return Err(FetchError::BlockHeaderUnavailable(
428						leaf_hash,
429						BlockHeaderUnavailableReason::SubsystemUnavailable,
430					)),
431			}
432		};
433
434		// If the node is a collator, bypass prospective-parachains. We're only interested in the
435		// one paraid and the subsystem is not present.
436		let min_relay_parents = if let Some(para_id) = self.collating_for {
437			fetch_min_relay_parents_for_collator(leaf_hash, leaf_header.number, sender)
438				.await?
439				.map(|x| vec![(para_id, x)])
440				.unwrap_or_default()
441		} else {
442			fetch_min_relay_parents_from_prospective_parachains(leaf_hash, sender).await?
443		};
444
445		let min_min = min_relay_parents.iter().map(|x| x.1).min().unwrap_or(leaf_header.number);
446		let expected_ancestry_len = (leaf_header.number.saturating_sub(min_min) as usize) + 1;
447
448		let ancestry = if leaf_header.number > 0 {
449			let mut next_ancestor_number = leaf_header.number - 1;
450			let mut next_ancestor_hash = leaf_header.parent_hash;
451
452			let mut ancestry = Vec::with_capacity(expected_ancestry_len);
453			ancestry.push(leaf_hash);
454
455			// Ensure all ancestors up to and including `min_min` are in the
456			// block storage. When views advance incrementally, everything
457			// should already be present.
458			while next_ancestor_number >= min_min {
459				let parent_hash = if let Some(info) =
460					self.block_info_storage.get(&next_ancestor_hash)
461				{
462					info.parent_hash
463				} else {
464					// load the header and insert into block storage.
465					let (tx, rx) = oneshot::channel();
466					sender.send_message(ChainApiMessage::BlockHeader(next_ancestor_hash, tx)).await;
467
468					let header = match rx.await {
469						Ok(Ok(Some(header))) => header,
470						Ok(Ok(None)) =>
471							return Err(FetchError::BlockHeaderUnavailable(
472								next_ancestor_hash,
473								BlockHeaderUnavailableReason::Unknown,
474							)),
475						Ok(Err(e)) =>
476							return Err(FetchError::BlockHeaderUnavailable(
477								next_ancestor_hash,
478								BlockHeaderUnavailableReason::Internal(e),
479							)),
480						Err(_) =>
481							return Err(FetchError::BlockHeaderUnavailable(
482								next_ancestor_hash,
483								BlockHeaderUnavailableReason::SubsystemUnavailable,
484							)),
485					};
486
487					self.block_info_storage.insert(
488						next_ancestor_hash,
489						BlockInfo {
490							block_number: next_ancestor_number,
491							parent_hash: header.parent_hash,
492							maybe_allowed_relay_parents: None,
493						},
494					);
495
496					header.parent_hash
497				};
498
499				ancestry.push(next_ancestor_hash);
500				if next_ancestor_number == 0 {
501					break
502				}
503
504				next_ancestor_number -= 1;
505				next_ancestor_hash = parent_hash;
506			}
507
508			ancestry
509		} else {
510			vec![leaf_hash]
511		};
512
513		let fetched_ancestry =
514			FetchSummary { minimum_ancestor_number: min_min, leaf_number: leaf_header.number };
515
516		let allowed_relay_parents = AllowedRelayParents {
517			minimum_relay_parents: min_relay_parents.into_iter().collect(),
518			allowed_relay_parents_contiguous: ancestry,
519		};
520
521		let leaf_block_info = BlockInfo {
522			parent_hash: leaf_header.parent_hash,
523			block_number: leaf_header.number,
524			maybe_allowed_relay_parents: Some(allowed_relay_parents),
525		};
526
527		self.block_info_storage.insert(leaf_hash, leaf_block_info);
528
529		Ok(fetched_ancestry)
530	}
531}
532
533/// Errors when fetching a leaf and associated ancestry.
534#[fatality::fatality]
535pub enum FetchError {
536	/// Activated leaf is already present in view.
537	#[error("Leaf was already known")]
538	AlreadyKnown,
539
540	/// Request to the prospective parachains subsystem failed.
541	#[error("The prospective parachains subsystem was unavailable")]
542	ProspectiveParachainsUnavailable,
543
544	/// Failed to fetch the block header.
545	#[error("A block header was unavailable")]
546	BlockHeaderUnavailable(Hash, BlockHeaderUnavailableReason),
547
548	/// A block header was unavailable due to a chain API error.
549	#[error("A block header was unavailable due to a chain API error")]
550	ChainApiError(Hash, ChainApiError),
551
552	/// Request to the Chain API subsystem failed.
553	#[error("The chain API subsystem was unavailable")]
554	ChainApiUnavailable,
555
556	/// Request to the runtime API failed.
557	#[error("Runtime API error: {0}")]
558	RuntimeApi(#[from] runtime::Error),
559}
560
561/// Reasons a block header might have been unavailable.
562#[derive(Debug)]
563pub enum BlockHeaderUnavailableReason {
564	/// Block header simply unknown.
565	Unknown,
566	/// Internal Chain API error.
567	Internal(ChainApiError),
568	/// The subsystem was unavailable.
569	SubsystemUnavailable,
570}
571
572struct FetchSummary {
573	minimum_ancestor_number: BlockNumber,
574	leaf_number: BlockNumber,
575}
576
577// Request the min relay parents from prospective-parachains.
578async fn fetch_min_relay_parents_from_prospective_parachains<
579	Sender: SubsystemSender<ProspectiveParachainsMessage>,
580>(
581	leaf_hash: Hash,
582	sender: &mut Sender,
583) -> Result<Vec<(ParaId, BlockNumber)>, FetchError> {
584	let (tx, rx) = oneshot::channel();
585	sender
586		.send_message(ProspectiveParachainsMessage::GetMinimumRelayParents(leaf_hash, tx))
587		.await;
588
589	rx.await.map_err(|_| FetchError::ProspectiveParachainsUnavailable)
590}
591
592// Request the min relay parent for the purposes of a collator, directly using ChainApi (where
593// prospective-parachains is not available).
594async fn fetch_min_relay_parents_for_collator<Sender>(
595	leaf_hash: Hash,
596	leaf_number: BlockNumber,
597	sender: &mut Sender,
598) -> Result<Option<BlockNumber>, FetchError>
599where
600	Sender: SubsystemSender<ProspectiveParachainsMessage>
601		+ SubsystemSender<RuntimeApiMessage>
602		+ SubsystemSender<ChainApiMessage>,
603{
604	// Fetch the session of the leaf. We must make sure that we stop at the ancestor which has a
605	// different session index.
606	let required_session =
607		recv_runtime(request_session_index_for_child(leaf_hash, sender).await).await?;
608
609	let scheduling_lookahead =
610		fetch_scheduling_lookahead(leaf_hash, required_session, sender).await?;
611
612	let mut min = leaf_number;
613
614	// Fetch the ancestors, up to (scheduling_lookahead - 1).
615	let (tx, rx) = oneshot::channel();
616	sender
617		.send_message(ChainApiMessage::Ancestors {
618			hash: leaf_hash,
619			k: scheduling_lookahead.saturating_sub(1) as usize,
620			response_channel: tx,
621		})
622		.await;
623	let hashes = rx
624		.await
625		.map_err(|_| FetchError::ChainApiUnavailable)?
626		.map_err(|err| FetchError::ChainApiError(leaf_hash, err))?;
627
628	for hash in hashes {
629		// The relay chain cannot accept blocks backed from previous sessions, with
630		// potentially previous validators. This is a technical limitation we need to
631		// respect here.
632		let session = recv_runtime(request_session_index_for_child(hash, sender).await).await?;
633
634		if session == required_session {
635			// We should never underflow here, the ChainAPI stops at genesis block.
636			min = min.saturating_sub(1);
637		} else {
638			break
639		}
640	}
641
642	Ok(Some(min))
643}
644
645#[cfg(test)]
646mod tests {
647	use super::*;
648	use crate::TimeoutExt;
649	use assert_matches::assert_matches;
650	use futures::future::{join, FutureExt};
651	use polkadot_node_subsystem::{messages::RuntimeApiRequest, AllMessages};
652	use polkadot_node_subsystem_test_helpers::{
653		make_subsystem_context, TestSubsystemContextHandle,
654	};
655	use polkadot_overseer::SubsystemContext;
656	use polkadot_primitives::Header;
657	use sp_core::testing::TaskExecutor;
658	use std::time::Duration;
659
660	const PARA_A: ParaId = ParaId::new(0);
661	const PARA_B: ParaId = ParaId::new(1);
662	const PARA_C: ParaId = ParaId::new(2);
663
664	const GENESIS_HASH: Hash = Hash::repeat_byte(0xFF);
665	const GENESIS_NUMBER: BlockNumber = 0;
666
667	// Chains A and B are forks of genesis.
668
669	const CHAIN_A: &[Hash] =
670		&[Hash::repeat_byte(0x01), Hash::repeat_byte(0x02), Hash::repeat_byte(0x03)];
671
672	const CHAIN_B: &[Hash] = &[
673		Hash::repeat_byte(0x04),
674		Hash::repeat_byte(0x05),
675		Hash::repeat_byte(0x06),
676		Hash::repeat_byte(0x07),
677		Hash::repeat_byte(0x08),
678		Hash::repeat_byte(0x09),
679	];
680
681	type VirtualOverseer = TestSubsystemContextHandle<AllMessages>;
682
683	const TIMEOUT: Duration = Duration::from_secs(2);
684
685	async fn overseer_recv(virtual_overseer: &mut VirtualOverseer) -> AllMessages {
686		virtual_overseer
687			.recv()
688			.timeout(TIMEOUT)
689			.await
690			.expect("overseer `recv` timed out")
691	}
692
693	fn default_header() -> Header {
694		Header {
695			parent_hash: Hash::zero(),
696			number: 0,
697			state_root: Hash::zero(),
698			extrinsics_root: Hash::zero(),
699			digest: Default::default(),
700		}
701	}
702
703	fn get_block_header(chain: &[Hash], hash: &Hash) -> Option<Header> {
704		let idx = chain.iter().position(|h| h == hash)?;
705		let parent_hash = idx.checked_sub(1).map(|i| chain[i]).unwrap_or(GENESIS_HASH);
706		let number =
707			if *hash == GENESIS_HASH { GENESIS_NUMBER } else { GENESIS_NUMBER + idx as u32 + 1 };
708		Some(Header { parent_hash, number, ..default_header() })
709	}
710
711	async fn assert_block_header_requests(
712		virtual_overseer: &mut VirtualOverseer,
713		chain: &[Hash],
714		blocks: &[Hash],
715	) {
716		for block in blocks.iter().rev() {
717			assert_matches!(
718				overseer_recv(virtual_overseer).await,
719				AllMessages::ChainApi(
720					ChainApiMessage::BlockHeader(hash, tx)
721				) => {
722					assert_eq!(*block, hash, "unexpected block header request");
723					let header = if block == &GENESIS_HASH {
724						Header {
725							number: GENESIS_NUMBER,
726							..default_header()
727						}
728					} else {
729						get_block_header(chain, block).expect("unknown block")
730					};
731
732					tx.send(Ok(Some(header))).unwrap();
733				}
734			);
735		}
736	}
737
738	async fn assert_min_relay_parents_request(
739		virtual_overseer: &mut VirtualOverseer,
740		leaf: &Hash,
741		response: Vec<(ParaId, u32)>,
742	) {
743		assert_matches!(
744			overseer_recv(virtual_overseer).await,
745			AllMessages::ProspectiveParachains(
746				ProspectiveParachainsMessage::GetMinimumRelayParents(
747					leaf_hash,
748					tx
749				)
750			) => {
751				assert_eq!(*leaf, leaf_hash, "received unexpected leaf hash");
752				tx.send(response).unwrap();
753			}
754		);
755	}
756
757	async fn assert_scheduling_lookahead_request(
758		virtual_overseer: &mut VirtualOverseer,
759		leaf: Hash,
760		lookahead: u32,
761	) {
762		assert_matches!(
763			overseer_recv(virtual_overseer).await,
764			AllMessages::RuntimeApi(
765				RuntimeApiMessage::Request(
766					leaf_hash,
767					RuntimeApiRequest::SchedulingLookahead(
768						_,
769						tx
770					)
771				)
772			) => {
773				assert_eq!(leaf, leaf_hash, "received unexpected leaf hash");
774				tx.send(Ok(lookahead)).unwrap();
775			}
776		);
777	}
778
779	async fn assert_session_index_request(
780		virtual_overseer: &mut VirtualOverseer,
781		leaf: Hash,
782		session: u32,
783	) {
784		assert_matches!(
785			overseer_recv(virtual_overseer).await,
786			AllMessages::RuntimeApi(
787				RuntimeApiMessage::Request(
788					leaf_hash,
789					RuntimeApiRequest::SessionIndexForChild(
790						tx
791					)
792				)
793			) => {
794				assert_eq!(leaf, leaf_hash, "received unexpected leaf hash");
795				tx.send(Ok(session)).unwrap();
796			}
797		);
798	}
799
800	async fn assert_ancestors_request(
801		virtual_overseer: &mut VirtualOverseer,
802		leaf: Hash,
803		expected_ancestor_len: u32,
804		response: Vec<Hash>,
805	) {
806		assert_matches!(
807			overseer_recv(virtual_overseer).await,
808			AllMessages::ChainApi(
809				ChainApiMessage::Ancestors {
810					hash: leaf_hash,
811					k,
812					response_channel: tx
813				}
814			) => {
815				assert_eq!(leaf, leaf_hash, "received unexpected leaf hash");
816				assert_eq!(k, expected_ancestor_len as usize);
817
818				tx.send(Ok(response)).unwrap();
819			}
820		);
821	}
822
823	#[test]
824	fn construct_fresh_view() {
825		let pool = TaskExecutor::new();
826		let (mut ctx, mut ctx_handle) = make_subsystem_context::<AllMessages, _>(pool);
827
828		let mut view = View::default();
829
830		assert_eq!(view.collating_for, None);
831
832		// Chain B.
833		const PARA_A_MIN_PARENT: u32 = 4;
834		const PARA_B_MIN_PARENT: u32 = 3;
835
836		let prospective_response = vec![(PARA_A, PARA_A_MIN_PARENT), (PARA_B, PARA_B_MIN_PARENT)];
837
838		let leaf = CHAIN_B.last().unwrap();
839		let leaf_idx = CHAIN_B.len() - 1;
840		let min_min_idx = (PARA_B_MIN_PARENT - GENESIS_NUMBER - 1) as usize;
841
842		let fut = view.activate_leaf(ctx.sender(), *leaf).timeout(TIMEOUT).map(|res| {
843			res.expect("`activate_leaf` timed out").unwrap();
844		});
845		let overseer_fut = async {
846			assert_block_header_requests(&mut ctx_handle, CHAIN_B, &CHAIN_B[leaf_idx..]).await;
847			assert_min_relay_parents_request(&mut ctx_handle, leaf, prospective_response).await;
848			assert_block_header_requests(&mut ctx_handle, CHAIN_B, &CHAIN_B[min_min_idx..leaf_idx])
849				.await;
850		};
851		futures::executor::block_on(join(fut, overseer_fut));
852
853		for i in min_min_idx..(CHAIN_B.len() - 1) {
854			// No allowed relay parents constructed for ancestry.
855			assert!(view.known_allowed_relay_parents_under(&CHAIN_B[i], None).is_none());
856		}
857
858		let leaf_info =
859			view.block_info_storage.get(leaf).expect("block must be present in storage");
860		assert_matches!(
861			leaf_info.maybe_allowed_relay_parents,
862			Some(ref allowed_relay_parents) => {
863				assert_eq!(allowed_relay_parents.minimum_relay_parents[&PARA_A], PARA_A_MIN_PARENT);
864				assert_eq!(allowed_relay_parents.minimum_relay_parents[&PARA_B], PARA_B_MIN_PARENT);
865				let expected_ancestry: Vec<Hash> =
866					CHAIN_B[min_min_idx..].iter().rev().copied().collect();
867				assert_eq!(
868					allowed_relay_parents.allowed_relay_parents_contiguous,
869					expected_ancestry
870				);
871
872				assert_eq!(view.known_allowed_relay_parents_under(&leaf, None), Some(&expected_ancestry[..]));
873				assert_eq!(view.known_allowed_relay_parents_under(&leaf, Some(PARA_A)), Some(&expected_ancestry[..(PARA_A_MIN_PARENT - 1) as usize]));
874				assert_eq!(view.known_allowed_relay_parents_under(&leaf, Some(PARA_B)), Some(&expected_ancestry[..]));
875				assert!(view.known_allowed_relay_parents_under(&leaf, Some(PARA_C)).unwrap().is_empty());
876
877				assert_eq!(view.leaves.len(), 1);
878				assert!(view.leaves.contains_key(leaf));
879				assert!(view.paths_via_relay_parent(&CHAIN_B[0]).is_empty());
880				assert!(view.paths_via_relay_parent(&CHAIN_A[0]).is_empty());
881				assert_eq!(
882					view.paths_via_relay_parent(&CHAIN_B[min_min_idx]),
883					vec![CHAIN_B[min_min_idx..].to_vec()]
884				);
885				assert_eq!(
886					view.paths_via_relay_parent(&CHAIN_B[min_min_idx + 1]),
887					vec![CHAIN_B[min_min_idx..].to_vec()]
888				);
889				assert_eq!(
890					view.paths_via_relay_parent(&leaf),
891					vec![CHAIN_B[min_min_idx..].to_vec()]
892				);
893			}
894		);
895
896		// Suppose the whole test chain A is allowed up to genesis for para C.
897		const PARA_C_MIN_PARENT: u32 = 0;
898		let prospective_response = vec![(PARA_C, PARA_C_MIN_PARENT)];
899		let leaf = CHAIN_A.last().unwrap();
900		let blocks = [&[GENESIS_HASH], CHAIN_A].concat();
901		let leaf_idx = blocks.len() - 1;
902
903		let fut = view.activate_leaf(ctx.sender(), *leaf).timeout(TIMEOUT).map(|res| {
904			res.expect("`activate_leaf` timed out").unwrap();
905		});
906		let overseer_fut = async {
907			assert_block_header_requests(&mut ctx_handle, CHAIN_A, &blocks[leaf_idx..]).await;
908			assert_min_relay_parents_request(&mut ctx_handle, leaf, prospective_response).await;
909			assert_block_header_requests(&mut ctx_handle, CHAIN_A, &blocks[..leaf_idx]).await;
910		};
911		futures::executor::block_on(join(fut, overseer_fut));
912
913		assert_eq!(view.leaves.len(), 2);
914
915		let leaf_info =
916			view.block_info_storage.get(leaf).expect("block must be present in storage");
917		assert_matches!(
918			leaf_info.maybe_allowed_relay_parents,
919			Some(ref allowed_relay_parents) => {
920				assert_eq!(allowed_relay_parents.minimum_relay_parents[&PARA_C], GENESIS_NUMBER);
921				let expected_ancestry: Vec<Hash> =
922					blocks[..].iter().rev().copied().collect();
923				assert_eq!(
924					allowed_relay_parents.allowed_relay_parents_contiguous,
925					expected_ancestry
926				);
927
928				assert_eq!(view.known_allowed_relay_parents_under(&leaf, None), Some(&expected_ancestry[..]));
929				assert_eq!(view.known_allowed_relay_parents_under(&leaf, Some(PARA_C)), Some(&expected_ancestry[..]));
930
931				assert!(view.known_allowed_relay_parents_under(&leaf, Some(PARA_A)).unwrap().is_empty());
932				assert!(view.known_allowed_relay_parents_under(&leaf, Some(PARA_B)).unwrap().is_empty());
933			}
934		);
935	}
936
937	#[test]
938	fn construct_fresh_view_single_para() {
939		let pool = TaskExecutor::new();
940		let (mut ctx, mut ctx_handle) = make_subsystem_context::<AllMessages, _>(pool);
941
942		let mut view = View::new(Some(PARA_A));
943
944		assert_eq!(view.collating_for, Some(PARA_A));
945
946		// Chain B.
947		const PARA_A_MIN_PARENT: u32 = 4;
948
949		let current_session = 2;
950
951		let leaf = CHAIN_B.last().unwrap();
952		let leaf_idx = CHAIN_B.len() - 1;
953		let min_min_idx = (PARA_A_MIN_PARENT - GENESIS_NUMBER - 1) as usize;
954
955		let fut = view.activate_leaf(ctx.sender(), *leaf).timeout(TIMEOUT).map(|res| {
956			res.expect("`activate_leaf` timed out").unwrap();
957		});
958		let overseer_fut = async {
959			assert_block_header_requests(&mut ctx_handle, CHAIN_B, &CHAIN_B[leaf_idx..]).await;
960
961			assert_session_index_request(&mut ctx_handle, *leaf, current_session).await;
962
963			assert_scheduling_lookahead_request(&mut ctx_handle, *leaf, PARA_A_MIN_PARENT + 1)
964				.await;
965
966			assert_ancestors_request(
967				&mut ctx_handle,
968				*leaf,
969				PARA_A_MIN_PARENT,
970				CHAIN_B[min_min_idx..leaf_idx].iter().copied().rev().collect(),
971			)
972			.await;
973
974			for hash in CHAIN_B[min_min_idx..leaf_idx].into_iter().rev() {
975				assert_session_index_request(&mut ctx_handle, *hash, current_session).await;
976			}
977
978			assert_block_header_requests(&mut ctx_handle, CHAIN_B, &CHAIN_B[min_min_idx..leaf_idx])
979				.await;
980		};
981		futures::executor::block_on(join(fut, overseer_fut));
982
983		for i in min_min_idx..(CHAIN_B.len() - 1) {
984			// No allowed relay parents constructed for ancestry.
985			assert!(view.known_allowed_relay_parents_under(&CHAIN_B[i], None).is_none());
986		}
987
988		let leaf_info =
989			view.block_info_storage.get(leaf).expect("block must be present in storage");
990		assert_matches!(
991			leaf_info.maybe_allowed_relay_parents,
992			Some(ref allowed_relay_parents) => {
993				assert_eq!(allowed_relay_parents.minimum_relay_parents[&PARA_A], PARA_A_MIN_PARENT);
994				let expected_ancestry: Vec<Hash> =
995					CHAIN_B[min_min_idx..].iter().rev().copied().collect();
996				assert_eq!(
997					allowed_relay_parents.allowed_relay_parents_contiguous,
998					expected_ancestry
999				);
1000
1001				assert_eq!(view.known_allowed_relay_parents_under(&leaf, None), Some(&expected_ancestry[..]));
1002				assert_eq!(view.known_allowed_relay_parents_under(&leaf, Some(PARA_A)), Some(&expected_ancestry[..]));
1003
1004				assert!(view.known_allowed_relay_parents_under(&leaf, Some(PARA_B)).unwrap().is_empty());
1005				assert!(view.known_allowed_relay_parents_under(&leaf, Some(PARA_C)).unwrap().is_empty());
1006
1007				assert!(view.paths_via_relay_parent(&CHAIN_A[0]).is_empty());
1008				assert_eq!(
1009					view.paths_via_relay_parent(&CHAIN_B[min_min_idx]),
1010					vec![CHAIN_B[min_min_idx..].to_vec()]
1011				);
1012			}
1013		);
1014
1015		// Suppose the whole test chain A is allowed up to genesis for para A, but the genesis block
1016		// is in a different session.
1017		let leaf = CHAIN_A.last().unwrap();
1018		let blocks = [&[GENESIS_HASH], CHAIN_A].concat();
1019		let leaf_idx = blocks.len() - 1;
1020
1021		let fut = view.activate_leaf(ctx.sender(), *leaf).timeout(TIMEOUT).map(|res| {
1022			res.expect("`activate_leaf` timed out").unwrap();
1023		});
1024
1025		let overseer_fut = async {
1026			assert_block_header_requests(&mut ctx_handle, CHAIN_A, &blocks[leaf_idx..]).await;
1027
1028			assert_session_index_request(&mut ctx_handle, *leaf, current_session).await;
1029
1030			assert_scheduling_lookahead_request(&mut ctx_handle, *leaf, blocks.len() as u32 + 1)
1031				.await;
1032
1033			assert_ancestors_request(
1034				&mut ctx_handle,
1035				*leaf,
1036				blocks.len() as u32,
1037				blocks[..leaf_idx].iter().rev().copied().collect(),
1038			)
1039			.await;
1040
1041			for hash in blocks[1..leaf_idx].into_iter().rev() {
1042				assert_session_index_request(&mut ctx_handle, *hash, current_session).await;
1043			}
1044
1045			assert_session_index_request(&mut ctx_handle, GENESIS_HASH, 0).await;
1046
1047			// We won't request for the genesis block
1048			assert_block_header_requests(&mut ctx_handle, CHAIN_A, &blocks[1..leaf_idx]).await;
1049		};
1050
1051		futures::executor::block_on(join(fut, overseer_fut));
1052
1053		assert_eq!(view.leaves.len(), 2);
1054
1055		let leaf_info =
1056			view.block_info_storage.get(leaf).expect("block must be present in storage");
1057		assert_matches!(
1058			leaf_info.maybe_allowed_relay_parents,
1059			Some(ref allowed_relay_parents) => {
1060				assert_eq!(allowed_relay_parents.minimum_relay_parents[&PARA_A], 1);
1061				let expected_ancestry: Vec<Hash> =
1062					CHAIN_A[..].iter().rev().copied().collect();
1063				assert_eq!(
1064					allowed_relay_parents.allowed_relay_parents_contiguous,
1065					expected_ancestry
1066				);
1067
1068				assert_eq!(view.known_allowed_relay_parents_under(&leaf, None), Some(&expected_ancestry[..]));
1069				assert_eq!(view.known_allowed_relay_parents_under(&leaf, Some(PARA_A)), Some(&expected_ancestry[..]));
1070
1071				assert!(view.known_allowed_relay_parents_under(&leaf, Some(PARA_B)).unwrap().is_empty());
1072				assert!(view.known_allowed_relay_parents_under(&leaf, Some(PARA_C)).unwrap().is_empty());
1073
1074				assert!(view.paths_via_relay_parent(&GENESIS_HASH).is_empty());
1075				assert_eq!(
1076					view.paths_via_relay_parent(&CHAIN_A[0]),
1077					vec![CHAIN_A.to_vec()]
1078				);
1079			}
1080		);
1081	}
1082
1083	#[test]
1084	fn reuse_block_info_storage() {
1085		let pool = TaskExecutor::new();
1086		let (mut ctx, mut ctx_handle) = make_subsystem_context::<AllMessages, _>(pool);
1087
1088		let mut view = View::default();
1089
1090		const PARA_A_MIN_PARENT: u32 = 1;
1091		let leaf_a_number = 3;
1092		let leaf_a = CHAIN_B[leaf_a_number - 1];
1093		let min_min_idx = (PARA_A_MIN_PARENT - GENESIS_NUMBER - 1) as usize;
1094
1095		let prospective_response = vec![(PARA_A, PARA_A_MIN_PARENT)];
1096
1097		let fut = view.activate_leaf(ctx.sender(), leaf_a).timeout(TIMEOUT).map(|res| {
1098			res.expect("`activate_leaf` timed out").unwrap();
1099		});
1100		let overseer_fut = async {
1101			assert_block_header_requests(
1102				&mut ctx_handle,
1103				CHAIN_B,
1104				&CHAIN_B[(leaf_a_number - 1)..leaf_a_number],
1105			)
1106			.await;
1107			assert_min_relay_parents_request(&mut ctx_handle, &leaf_a, prospective_response).await;
1108			assert_block_header_requests(
1109				&mut ctx_handle,
1110				CHAIN_B,
1111				&CHAIN_B[min_min_idx..(leaf_a_number - 1)],
1112			)
1113			.await;
1114		};
1115		futures::executor::block_on(join(fut, overseer_fut));
1116
1117		// Blocks up to the 3rd are present in storage.
1118		const PARA_B_MIN_PARENT: u32 = 2;
1119		let leaf_b_number = 5;
1120		let leaf_b = CHAIN_B[leaf_b_number - 1];
1121
1122		let prospective_response = vec![(PARA_B, PARA_B_MIN_PARENT)];
1123
1124		let fut = view.activate_leaf(ctx.sender(), leaf_b).timeout(TIMEOUT).map(|res| {
1125			res.expect("`activate_leaf` timed out").unwrap();
1126		});
1127		let overseer_fut = async {
1128			assert_block_header_requests(
1129				&mut ctx_handle,
1130				CHAIN_B,
1131				&CHAIN_B[(leaf_b_number - 1)..leaf_b_number],
1132			)
1133			.await;
1134			assert_min_relay_parents_request(&mut ctx_handle, &leaf_b, prospective_response).await;
1135			assert_block_header_requests(
1136				&mut ctx_handle,
1137				CHAIN_B,
1138				&CHAIN_B[leaf_a_number..(leaf_b_number - 1)], // Note the expected range.
1139			)
1140			.await;
1141		};
1142		futures::executor::block_on(join(fut, overseer_fut));
1143
1144		// Allowed relay parents for leaf A are preserved.
1145		let leaf_a_info =
1146			view.block_info_storage.get(&leaf_a).expect("block must be present in storage");
1147		assert_matches!(
1148			leaf_a_info.maybe_allowed_relay_parents,
1149			Some(ref allowed_relay_parents) => {
1150				assert_eq!(allowed_relay_parents.minimum_relay_parents[&PARA_A], PARA_A_MIN_PARENT);
1151				let expected_ancestry: Vec<Hash> =
1152					CHAIN_B[min_min_idx..leaf_a_number].iter().rev().copied().collect();
1153				let ancestry = view.known_allowed_relay_parents_under(&leaf_a, Some(PARA_A)).unwrap().to_vec();
1154				assert_eq!(ancestry, expected_ancestry);
1155			}
1156		);
1157	}
1158
1159	#[test]
1160	fn pruning() {
1161		let pool = TaskExecutor::new();
1162		let (mut ctx, mut ctx_handle) = make_subsystem_context::<AllMessages, _>(pool);
1163
1164		let mut view = View::default();
1165
1166		const PARA_A_MIN_PARENT: u32 = 3;
1167		let leaf_a = CHAIN_B.iter().rev().nth(1).unwrap();
1168		let leaf_a_idx = CHAIN_B.len() - 2;
1169		let min_a_idx = (PARA_A_MIN_PARENT - GENESIS_NUMBER - 1) as usize;
1170
1171		let prospective_response = vec![(PARA_A, PARA_A_MIN_PARENT)];
1172
1173		let fut = view
1174			.activate_leaf(ctx.sender(), *leaf_a)
1175			.timeout(TIMEOUT)
1176			.map(|res| res.unwrap().unwrap());
1177		let overseer_fut = async {
1178			assert_block_header_requests(
1179				&mut ctx_handle,
1180				CHAIN_B,
1181				&CHAIN_B[leaf_a_idx..(leaf_a_idx + 1)],
1182			)
1183			.await;
1184			assert_min_relay_parents_request(&mut ctx_handle, &leaf_a, prospective_response).await;
1185			assert_block_header_requests(&mut ctx_handle, CHAIN_B, &CHAIN_B[min_a_idx..leaf_a_idx])
1186				.await;
1187		};
1188		futures::executor::block_on(join(fut, overseer_fut));
1189
1190		// Also activate a leaf with a lesser minimum relay parent.
1191		const PARA_B_MIN_PARENT: u32 = 2;
1192		let leaf_b = CHAIN_B.last().unwrap();
1193		let min_b_idx = (PARA_B_MIN_PARENT - GENESIS_NUMBER - 1) as usize;
1194
1195		let prospective_response = vec![(PARA_B, PARA_B_MIN_PARENT)];
1196		// Headers will be requested for the minimum block and the leaf.
1197		let blocks = &[CHAIN_B[min_b_idx], *leaf_b];
1198
1199		let fut = view
1200			.activate_leaf(ctx.sender(), *leaf_b)
1201			.timeout(TIMEOUT)
1202			.map(|res| res.expect("`activate_leaf` timed out").unwrap());
1203		let overseer_fut = async {
1204			assert_block_header_requests(&mut ctx_handle, CHAIN_B, &blocks[(blocks.len() - 1)..])
1205				.await;
1206			assert_min_relay_parents_request(&mut ctx_handle, &leaf_b, prospective_response).await;
1207			assert_block_header_requests(&mut ctx_handle, CHAIN_B, &blocks[..(blocks.len() - 1)])
1208				.await;
1209		};
1210		futures::executor::block_on(join(fut, overseer_fut));
1211
1212		// Prune implicit ancestor (no-op).
1213		let block_info_len = view.block_info_storage.len();
1214		view.deactivate_leaf(CHAIN_B[leaf_a_idx - 1]);
1215		assert_eq!(block_info_len, view.block_info_storage.len());
1216
1217		// Prune a leaf with a greater minimum relay parent.
1218		view.deactivate_leaf(*leaf_b);
1219		for hash in CHAIN_B.iter().take(PARA_B_MIN_PARENT as usize) {
1220			assert!(!view.block_info_storage.contains_key(hash));
1221		}
1222
1223		// Prune the last leaf.
1224		view.deactivate_leaf(*leaf_a);
1225		assert!(view.block_info_storage.is_empty());
1226	}
1227
1228	#[test]
1229	fn genesis_ancestry() {
1230		let pool = TaskExecutor::new();
1231		let (mut ctx, mut ctx_handle) = make_subsystem_context::<AllMessages, _>(pool);
1232
1233		let mut view = View::default();
1234
1235		const PARA_A_MIN_PARENT: u32 = 0;
1236
1237		let prospective_response = vec![(PARA_A, PARA_A_MIN_PARENT)];
1238		let fut = view.activate_leaf(ctx.sender(), GENESIS_HASH).timeout(TIMEOUT).map(|res| {
1239			res.expect("`activate_leaf` timed out").unwrap();
1240		});
1241		let overseer_fut = async {
1242			assert_block_header_requests(&mut ctx_handle, &[GENESIS_HASH], &[GENESIS_HASH]).await;
1243			assert_min_relay_parents_request(&mut ctx_handle, &GENESIS_HASH, prospective_response)
1244				.await;
1245		};
1246		futures::executor::block_on(join(fut, overseer_fut));
1247
1248		assert_matches!(
1249			view.known_allowed_relay_parents_under(&GENESIS_HASH, None),
1250			Some(hashes) if hashes == &[GENESIS_HASH]
1251		);
1252	}
1253
1254	#[test]
1255	fn path_with_fork() {
1256		let pool = TaskExecutor::new();
1257		let (mut ctx, mut ctx_handle) = make_subsystem_context::<AllMessages, _>(pool);
1258
1259		let mut view = View::default();
1260
1261		assert_eq!(view.collating_for, None);
1262
1263		// Chain A
1264		let prospective_response = vec![(PARA_A, 0)]; // was PARA_A_MIN_PARENT
1265		let leaf = CHAIN_A.last().unwrap();
1266		let blocks = [&[GENESIS_HASH], CHAIN_A].concat();
1267		let leaf_idx = blocks.len() - 1;
1268
1269		let fut = view.activate_leaf(ctx.sender(), *leaf).timeout(TIMEOUT).map(|res| {
1270			res.expect("`activate_leaf` timed out").unwrap();
1271		});
1272		let overseer_fut = async {
1273			assert_block_header_requests(&mut ctx_handle, CHAIN_A, &blocks[leaf_idx..]).await;
1274			assert_min_relay_parents_request(&mut ctx_handle, leaf, prospective_response).await;
1275			assert_block_header_requests(&mut ctx_handle, CHAIN_A, &blocks[..leaf_idx]).await;
1276		};
1277		futures::executor::block_on(join(fut, overseer_fut));
1278
1279		// Chain B
1280		let prospective_response = vec![(PARA_A, 1)];
1281
1282		let leaf = CHAIN_B.last().unwrap();
1283		let leaf_idx = CHAIN_B.len() - 1;
1284
1285		let fut = view.activate_leaf(ctx.sender(), *leaf).timeout(TIMEOUT).map(|res| {
1286			res.expect("`activate_leaf` timed out").unwrap();
1287		});
1288		let overseer_fut = async {
1289			assert_block_header_requests(&mut ctx_handle, CHAIN_B, &CHAIN_B[leaf_idx..]).await;
1290			assert_min_relay_parents_request(&mut ctx_handle, leaf, prospective_response).await;
1291			assert_block_header_requests(&mut ctx_handle, CHAIN_B, &CHAIN_B[0..leaf_idx]).await;
1292		};
1293		futures::executor::block_on(join(fut, overseer_fut));
1294
1295		assert_eq!(view.leaves.len(), 2);
1296
1297		let mut paths_to_genesis = view.paths_via_relay_parent(&GENESIS_HASH);
1298		paths_to_genesis.sort();
1299		let mut expected_paths_to_genesis = vec![
1300			[GENESIS_HASH].iter().chain(CHAIN_A.iter()).copied().collect::<Vec<_>>(),
1301			[GENESIS_HASH].iter().chain(CHAIN_B.iter()).copied().collect::<Vec<_>>(),
1302		];
1303		expected_paths_to_genesis.sort();
1304		assert_eq!(paths_to_genesis, expected_paths_to_genesis);
1305
1306		let path_to_leaf_in_a = view.paths_via_relay_parent(&CHAIN_A[1]);
1307		let expected_path_to_leaf_in_a =
1308			vec![[GENESIS_HASH].iter().chain(CHAIN_A.iter()).copied().collect::<Vec<_>>()];
1309		assert_eq!(path_to_leaf_in_a, expected_path_to_leaf_in_a);
1310
1311		let path_to_leaf_in_b = view.paths_via_relay_parent(&CHAIN_B[4]);
1312		let expected_path_to_leaf_in_b =
1313			vec![[GENESIS_HASH].iter().chain(CHAIN_B.iter()).copied().collect::<Vec<_>>()];
1314		assert_eq!(path_to_leaf_in_b, expected_path_to_leaf_in_b);
1315
1316		assert_eq!(view.paths_via_relay_parent(&Hash::repeat_byte(0x0A)), Vec::<Vec<Hash>>::new());
1317	}
1318}