1use 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
34const MINIMUM_RETAIN_LENGTH: BlockNumber = 2;
36
37#[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 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#[derive(Debug, Clone)]
65struct AllowedRelayParents {
66 minimum_relay_parents: HashMap<ParaId, BlockNumber>,
70 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(¶_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 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 retain_minimum: BlockNumber,
108}
109
110#[derive(Debug, Clone)]
111struct BlockInfo {
112 block_number: BlockNumber,
113 maybe_allowed_relay_parents: Option<AllowedRelayParents>,
123 parent_hash: Hash,
124}
125
126#[derive(Debug, Clone, PartialEq)]
129pub struct BlockInfoProspectiveParachains {
130 pub hash: Hash,
132 pub parent_hash: Hash,
134 pub number: BlockNumber,
136 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 pub fn leaves(&self) -> impl Iterator<Item = &Hash> {
149 self.leaves.keys()
150 }
151
152 pub fn contains_leaf(&self, leaf_hash: &Hash) -> bool {
154 self.leaves.contains_key(leaf_hash)
155 }
156
157 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 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 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 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 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 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 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 {
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 pub fn all_allowed_relay_parents(&self) -> impl Iterator<Item = &Hash> {
301 self.block_info_storage.keys()
302 }
303
304 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 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 return vec![]
347 };
348
349 if !self.block_info_storage.contains_key(relay_parent) {
350 return vec![]
352 }
353
354 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 loop {
364 if visited.contains(¤t_leaf) {
365 break
367 }
368
369 current_leaf = match self.block_info_storage.get(¤t_leaf) {
370 Some(info) => {
371 path.push(current_leaf);
374 visited.insert(current_leaf);
375
376 if current_leaf == *relay_parent {
379 path_contains_target = true;
380 }
381
382 info.parent_hash
384 },
385 None => {
386 if path_contains_target {
388 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 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 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 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#[fatality::fatality]
535pub enum FetchError {
536 #[error("Leaf was already known")]
538 AlreadyKnown,
539
540 #[error("The prospective parachains subsystem was unavailable")]
542 ProspectiveParachainsUnavailable,
543
544 #[error("A block header was unavailable")]
546 BlockHeaderUnavailable(Hash, BlockHeaderUnavailableReason),
547
548 #[error("A block header was unavailable due to a chain API error")]
550 ChainApiError(Hash, ChainApiError),
551
552 #[error("The chain API subsystem was unavailable")]
554 ChainApiUnavailable,
555
556 #[error("Runtime API error: {0}")]
558 RuntimeApi(#[from] runtime::Error),
559}
560
561#[derive(Debug)]
563pub enum BlockHeaderUnavailableReason {
564 Unknown,
566 Internal(ChainApiError),
568 SubsystemUnavailable,
570}
571
572struct FetchSummary {
573 minimum_ancestor_number: BlockNumber,
574 leaf_number: BlockNumber,
575}
576
577async 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
592async 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 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 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 let session = recv_runtime(request_session_index_for_child(hash, sender).await).await?;
633
634 if session == required_session {
635 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 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 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 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 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 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 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 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 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 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)], )
1140 .await;
1141 };
1142 futures::executor::block_on(join(fut, overseer_fut));
1143
1144 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 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 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 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 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 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 let prospective_response = vec![(PARA_A, 0)]; 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 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}