Files
pezkuwi-subxt/polkadot/node/core/prospective-parachains/src/tests.rs
T
Alexander Samusev cd63276d31 [ci] Update rust to 1.74 (#2545)
cc https://github.com/paritytech/ci_cd/issues/900

---------

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Dónal Murray <donal.murray@parity.io>
Co-authored-by: Bastian Köcher <info@kchr.de>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: command-bot <>
2023-12-07 09:57:43 +01:00

1626 lines
44 KiB
Rust

// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use super::*;
use assert_matches::assert_matches;
use polkadot_node_subsystem::{
errors::RuntimeApiError,
messages::{
AllMessages, HypotheticalFrontierRequest, ProspectiveParachainsMessage,
ProspectiveValidationDataRequest,
},
};
use polkadot_node_subsystem_test_helpers as test_helpers;
use polkadot_primitives::{
async_backing::{AsyncBackingParams, BackingState, Constraints, InboundHrmpLimitations},
CommittedCandidateReceipt, HeadData, Header, PersistedValidationData, ScheduledCore,
ValidationCodeHash,
};
use polkadot_primitives_test_helpers::make_candidate;
use std::sync::Arc;
use test_helpers::mock::new_leaf;
const ALLOWED_ANCESTRY_LEN: u32 = 3;
const ASYNC_BACKING_PARAMETERS: AsyncBackingParams =
AsyncBackingParams { max_candidate_depth: 4, allowed_ancestry_len: ALLOWED_ANCESTRY_LEN };
const ASYNC_BACKING_DISABLED_ERROR: RuntimeApiError =
RuntimeApiError::NotSupported { runtime_api_name: "test-runtime" };
const MAX_POV_SIZE: u32 = 1_000_000;
type VirtualOverseer = test_helpers::TestSubsystemContextHandle<ProspectiveParachainsMessage>;
fn dummy_constraints(
min_relay_parent_number: BlockNumber,
valid_watermarks: Vec<BlockNumber>,
required_parent: HeadData,
validation_code_hash: ValidationCodeHash,
) -> Constraints {
Constraints {
min_relay_parent_number,
max_pov_size: MAX_POV_SIZE,
max_code_size: 1_000_000,
ump_remaining: 10,
ump_remaining_bytes: 1_000,
max_ump_num_per_candidate: 10,
dmp_remaining_messages: vec![],
hrmp_inbound: InboundHrmpLimitations { valid_watermarks },
hrmp_channels_out: vec![],
max_hrmp_num_per_candidate: 0,
required_parent,
validation_code_hash,
upgrade_restriction: None,
future_validation_code: None,
}
}
struct TestState {
availability_cores: Vec<CoreState>,
validation_code_hash: ValidationCodeHash,
}
impl Default for TestState {
fn default() -> Self {
let chain_a = ParaId::from(1);
let chain_b = ParaId::from(2);
let availability_cores = vec![
CoreState::Scheduled(ScheduledCore { para_id: chain_a, collator: None }),
CoreState::Scheduled(ScheduledCore { para_id: chain_b, collator: None }),
];
let validation_code_hash = Hash::repeat_byte(42).into();
Self { availability_cores, validation_code_hash }
}
}
fn get_parent_hash(hash: Hash) -> Hash {
Hash::from_low_u64_be(hash.to_low_u64_be() + 1)
}
fn test_harness<T: Future<Output = VirtualOverseer>>(
test: impl FnOnce(VirtualOverseer) -> T,
) -> View {
let pool = sp_core::testing::TaskExecutor::new();
let (mut context, virtual_overseer) = test_helpers::make_subsystem_context(pool.clone());
let mut view = View::new();
let subsystem = async move {
match run_iteration(&mut context, &mut view, &Metrics(None)).await {
Ok(()) => {},
Err(e) => panic!("{:?}", e),
}
view
};
let test_fut = test(virtual_overseer);
futures::pin_mut!(test_fut);
futures::pin_mut!(subsystem);
let (_, view) = futures::executor::block_on(future::join(
async move {
let mut virtual_overseer = test_fut.await;
virtual_overseer.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await;
},
subsystem,
));
view
}
#[derive(Debug, Clone)]
struct PerParaData {
min_relay_parent: BlockNumber,
head_data: HeadData,
pending_availability: Vec<CandidatePendingAvailability>,
}
impl PerParaData {
pub fn new(min_relay_parent: BlockNumber, head_data: HeadData) -> Self {
Self { min_relay_parent, head_data, pending_availability: Vec::new() }
}
pub fn new_with_pending(
min_relay_parent: BlockNumber,
head_data: HeadData,
pending: Vec<CandidatePendingAvailability>,
) -> Self {
Self { min_relay_parent, head_data, pending_availability: pending }
}
}
struct TestLeaf {
number: BlockNumber,
hash: Hash,
para_data: Vec<(ParaId, PerParaData)>,
}
impl TestLeaf {
pub fn para_data(&self, para_id: ParaId) -> &PerParaData {
self.para_data
.iter()
.find_map(|(p_id, data)| if *p_id == para_id { Some(data) } else { None })
.unwrap()
}
}
async fn send_block_header(virtual_overseer: &mut VirtualOverseer, hash: Hash, number: u32) {
let header = Header {
parent_hash: get_parent_hash(hash),
number,
state_root: Hash::zero(),
extrinsics_root: Hash::zero(),
digest: Default::default(),
};
assert_matches!(
virtual_overseer.recv().await,
AllMessages::ChainApi(
ChainApiMessage::BlockHeader(parent, tx)
) if parent == hash => {
tx.send(Ok(Some(header))).unwrap();
}
);
}
async fn activate_leaf(
virtual_overseer: &mut VirtualOverseer,
leaf: &TestLeaf,
test_state: &TestState,
) {
activate_leaf_with_params(virtual_overseer, leaf, test_state, ASYNC_BACKING_PARAMETERS).await;
}
async fn activate_leaf_with_params(
virtual_overseer: &mut VirtualOverseer,
leaf: &TestLeaf,
test_state: &TestState,
async_backing_params: AsyncBackingParams,
) {
let TestLeaf { number, hash, .. } = leaf;
let activated = new_leaf(*hash, *number);
virtual_overseer
.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(
activated,
))))
.await;
handle_leaf_activation(virtual_overseer, leaf, test_state, async_backing_params).await;
}
async fn handle_leaf_activation(
virtual_overseer: &mut VirtualOverseer,
leaf: &TestLeaf,
test_state: &TestState,
async_backing_params: AsyncBackingParams,
) {
let TestLeaf { number, hash, para_data } = leaf;
assert_matches!(
virtual_overseer.recv().await,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(parent, RuntimeApiRequest::AsyncBackingParams(tx))
) if parent == *hash => {
tx.send(Ok(async_backing_params)).unwrap();
}
);
assert_matches!(
virtual_overseer.recv().await,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(parent, RuntimeApiRequest::AvailabilityCores(tx))
) if parent == *hash => {
tx.send(Ok(test_state.availability_cores.clone())).unwrap();
}
);
send_block_header(virtual_overseer, *hash, *number).await;
// Check that subsystem job issues a request for ancestors.
let min_min = para_data.iter().map(|(_, data)| data.min_relay_parent).min().unwrap_or(*number);
let ancestry_len = number - min_min;
let ancestry_hashes: Vec<Hash> =
std::iter::successors(Some(*hash), |h| Some(get_parent_hash(*h)))
.skip(1)
.take(ancestry_len as usize)
.collect();
let ancestry_numbers = (min_min..*number).rev();
let ancestry_iter = ancestry_hashes.clone().into_iter().zip(ancestry_numbers).peekable();
if ancestry_len > 0 {
assert_matches!(
virtual_overseer.recv().await,
AllMessages::ChainApi(
ChainApiMessage::Ancestors{hash: block_hash, k, response_channel: tx}
) if block_hash == *hash && k == ALLOWED_ANCESTRY_LEN as usize => {
tx.send(Ok(ancestry_hashes.clone())).unwrap();
}
);
assert_matches!(
virtual_overseer.recv().await,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(parent, RuntimeApiRequest::SessionIndexForChild(tx))
) if parent == *hash => {
tx.send(Ok(1)).unwrap();
}
);
}
for (hash, number) in ancestry_iter {
send_block_header(virtual_overseer, hash, number).await;
assert_matches!(
virtual_overseer.recv().await,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(parent, RuntimeApiRequest::SessionIndexForChild(tx))
) if parent == hash => {
tx.send(Ok(1)).unwrap();
}
);
}
for _ in 0..test_state.availability_cores.len() {
let message = virtual_overseer.recv().await;
// Get the para we are working with since the order is not deterministic.
let para_id = match message {
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
_,
RuntimeApiRequest::ParaBackingState(p_id, _),
)) => p_id,
_ => panic!("received unexpected message {:?}", message),
};
let PerParaData { min_relay_parent, head_data, pending_availability } =
leaf.para_data(para_id);
let constraints = dummy_constraints(
*min_relay_parent,
vec![*number],
head_data.clone(),
test_state.validation_code_hash,
);
let backing_state =
BackingState { constraints, pending_availability: pending_availability.clone() };
assert_matches!(
message,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(parent, RuntimeApiRequest::ParaBackingState(p_id, tx))
) if parent == *hash && p_id == para_id => {
tx.send(Ok(Some(backing_state))).unwrap();
}
);
for pending in pending_availability {
send_block_header(
virtual_overseer,
pending.descriptor.relay_parent,
pending.relay_parent_number,
)
.await;
}
}
// Get minimum relay parents.
let (tx, rx) = oneshot::channel();
virtual_overseer
.send(overseer::FromOrchestra::Communication {
msg: ProspectiveParachainsMessage::GetMinimumRelayParents(*hash, tx),
})
.await;
let mut resp = rx.await.unwrap();
resp.sort();
let mrp_response: Vec<(ParaId, BlockNumber)> = para_data
.iter()
.map(|(para_id, data)| (*para_id, data.min_relay_parent))
.collect();
assert_eq!(resp, mrp_response);
}
async fn deactivate_leaf(virtual_overseer: &mut VirtualOverseer, hash: Hash) {
virtual_overseer
.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::stop_work(
hash,
))))
.await;
}
async fn introduce_candidate(
virtual_overseer: &mut VirtualOverseer,
candidate: CommittedCandidateReceipt,
pvd: PersistedValidationData,
) {
let req = IntroduceCandidateRequest {
candidate_para: candidate.descriptor().para_id,
candidate_receipt: candidate,
persisted_validation_data: pvd,
};
let (tx, _) = oneshot::channel();
virtual_overseer
.send(overseer::FromOrchestra::Communication {
msg: ProspectiveParachainsMessage::IntroduceCandidate(req, tx),
})
.await;
}
async fn second_candidate(
virtual_overseer: &mut VirtualOverseer,
candidate: CommittedCandidateReceipt,
) {
virtual_overseer
.send(overseer::FromOrchestra::Communication {
msg: ProspectiveParachainsMessage::CandidateSeconded(
candidate.descriptor.para_id,
candidate.hash(),
),
})
.await;
}
async fn back_candidate(
virtual_overseer: &mut VirtualOverseer,
candidate: &CommittedCandidateReceipt,
candidate_hash: CandidateHash,
) {
virtual_overseer
.send(overseer::FromOrchestra::Communication {
msg: ProspectiveParachainsMessage::CandidateBacked(
candidate.descriptor.para_id,
candidate_hash,
),
})
.await;
}
async fn get_membership(
virtual_overseer: &mut VirtualOverseer,
para_id: ParaId,
candidate_hash: CandidateHash,
expected_membership_response: Vec<(Hash, Vec<usize>)>,
) {
let (tx, rx) = oneshot::channel();
virtual_overseer
.send(overseer::FromOrchestra::Communication {
msg: ProspectiveParachainsMessage::GetTreeMembership(para_id, candidate_hash, tx),
})
.await;
let resp = rx.await.unwrap();
assert_eq!(resp, expected_membership_response);
}
async fn get_backable_candidate(
virtual_overseer: &mut VirtualOverseer,
leaf: &TestLeaf,
para_id: ParaId,
required_path: Vec<CandidateHash>,
expected_result: Option<(CandidateHash, Hash)>,
) {
let (tx, rx) = oneshot::channel();
virtual_overseer
.send(overseer::FromOrchestra::Communication {
msg: ProspectiveParachainsMessage::GetBackableCandidate(
leaf.hash,
para_id,
required_path,
tx,
),
})
.await;
let resp = rx.await.unwrap();
assert_eq!(resp, expected_result);
}
async fn get_hypothetical_frontier(
virtual_overseer: &mut VirtualOverseer,
candidate_hash: CandidateHash,
receipt: CommittedCandidateReceipt,
persisted_validation_data: PersistedValidationData,
fragment_tree_relay_parent: Hash,
backed_in_path_only: bool,
expected_depths: Vec<usize>,
) {
let hypothetical_candidate = HypotheticalCandidate::Complete {
candidate_hash,
receipt: Arc::new(receipt),
persisted_validation_data,
};
let request = HypotheticalFrontierRequest {
candidates: vec![hypothetical_candidate.clone()],
fragment_tree_relay_parent: Some(fragment_tree_relay_parent),
backed_in_path_only,
};
let (tx, rx) = oneshot::channel();
virtual_overseer
.send(overseer::FromOrchestra::Communication {
msg: ProspectiveParachainsMessage::GetHypotheticalFrontier(request, tx),
})
.await;
let resp = rx.await.unwrap();
let expected_frontier = if expected_depths.is_empty() {
vec![(hypothetical_candidate, vec![])]
} else {
vec![(hypothetical_candidate, vec![(fragment_tree_relay_parent, expected_depths)])]
};
assert_eq!(resp, expected_frontier);
}
async fn get_pvd(
virtual_overseer: &mut VirtualOverseer,
para_id: ParaId,
candidate_relay_parent: Hash,
parent_head_data: HeadData,
expected_pvd: Option<PersistedValidationData>,
) {
let request = ProspectiveValidationDataRequest {
para_id,
candidate_relay_parent,
parent_head_data_hash: parent_head_data.hash(),
};
let (tx, rx) = oneshot::channel();
virtual_overseer
.send(overseer::FromOrchestra::Communication {
msg: ProspectiveParachainsMessage::GetProspectiveValidationData(request, tx),
})
.await;
let resp = rx.await.unwrap();
assert_eq!(resp, expected_pvd);
}
#[test]
fn should_do_no_work_if_async_backing_disabled_for_leaf() {
async fn activate_leaf_async_backing_disabled(virtual_overseer: &mut VirtualOverseer) {
let hash = Hash::from_low_u64_be(130);
// Start work on some new parent.
virtual_overseer
.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(
ActiveLeavesUpdate::start_work(new_leaf(hash, 1)),
)))
.await;
assert_matches!(
virtual_overseer.recv().await,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(parent, RuntimeApiRequest::AsyncBackingParams(tx))
) if parent == hash => {
tx.send(Err(ASYNC_BACKING_DISABLED_ERROR)).unwrap();
}
);
}
let view = test_harness(|mut virtual_overseer| async move {
activate_leaf_async_backing_disabled(&mut virtual_overseer).await;
virtual_overseer
});
assert!(view.active_leaves.is_empty());
assert!(view.candidate_storage.is_empty());
}
// Send some candidates and make sure all are found:
// - Two for the same leaf A
// - One for leaf B on parachain 1
// - One for leaf C on parachain 2
#[test]
fn send_candidates_and_check_if_found() {
let test_state = TestState::default();
let view = test_harness(|mut virtual_overseer| async move {
// Leaf A
let leaf_a = TestLeaf {
number: 100,
hash: Hash::from_low_u64_be(130),
para_data: vec![
(1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))),
(2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))),
],
};
// Leaf B
let leaf_b = TestLeaf {
number: 101,
hash: Hash::from_low_u64_be(131),
para_data: vec![
(1.into(), PerParaData::new(99, HeadData(vec![3, 4, 5]))),
(2.into(), PerParaData::new(101, HeadData(vec![4, 5, 6]))),
],
};
// Leaf C
let leaf_c = TestLeaf {
number: 102,
hash: Hash::from_low_u64_be(132),
para_data: vec![
(1.into(), PerParaData::new(102, HeadData(vec![5, 6, 7]))),
(2.into(), PerParaData::new(98, HeadData(vec![6, 7, 8]))),
],
};
// Activate leaves.
activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;
activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await;
activate_leaf(&mut virtual_overseer, &leaf_c, &test_state).await;
// Candidate A1
let (candidate_a1, pvd_a1) = make_candidate(
leaf_a.hash,
leaf_a.number,
1.into(),
HeadData(vec![1, 2, 3]),
HeadData(vec![1]),
test_state.validation_code_hash,
);
let candidate_hash_a1 = candidate_a1.hash();
let response_a1 = vec![(leaf_a.hash, vec![0])];
// Candidate A2
let (candidate_a2, pvd_a2) = make_candidate(
leaf_a.hash,
leaf_a.number,
2.into(),
HeadData(vec![2, 3, 4]),
HeadData(vec![2]),
test_state.validation_code_hash,
);
let candidate_hash_a2 = candidate_a2.hash();
let response_a2 = vec![(leaf_a.hash, vec![0])];
// Candidate B
let (candidate_b, pvd_b) = make_candidate(
leaf_b.hash,
leaf_b.number,
1.into(),
HeadData(vec![3, 4, 5]),
HeadData(vec![3]),
test_state.validation_code_hash,
);
let candidate_hash_b = candidate_b.hash();
let response_b = vec![(leaf_b.hash, vec![0])];
// Candidate C
let (candidate_c, pvd_c) = make_candidate(
leaf_c.hash,
leaf_c.number,
2.into(),
HeadData(vec![6, 7, 8]),
HeadData(vec![4]),
test_state.validation_code_hash,
);
let candidate_hash_c = candidate_c.hash();
let response_c = vec![(leaf_c.hash, vec![0])];
// Introduce candidates.
introduce_candidate(&mut virtual_overseer, candidate_a1, pvd_a1).await;
introduce_candidate(&mut virtual_overseer, candidate_a2, pvd_a2).await;
introduce_candidate(&mut virtual_overseer, candidate_b, pvd_b).await;
introduce_candidate(&mut virtual_overseer, candidate_c, pvd_c).await;
// Check candidate tree membership.
get_membership(&mut virtual_overseer, 1.into(), candidate_hash_a1, response_a1).await;
get_membership(&mut virtual_overseer, 2.into(), candidate_hash_a2, response_a2).await;
get_membership(&mut virtual_overseer, 1.into(), candidate_hash_b, response_b).await;
get_membership(&mut virtual_overseer, 2.into(), candidate_hash_c, response_c).await;
// The candidates should not be found on other parachains.
get_membership(&mut virtual_overseer, 2.into(), candidate_hash_a1, vec![]).await;
get_membership(&mut virtual_overseer, 1.into(), candidate_hash_a2, vec![]).await;
get_membership(&mut virtual_overseer, 2.into(), candidate_hash_b, vec![]).await;
get_membership(&mut virtual_overseer, 1.into(), candidate_hash_c, vec![]).await;
virtual_overseer
});
assert_eq!(view.active_leaves.len(), 3);
assert_eq!(view.candidate_storage.len(), 2);
// Two parents and two candidates per para.
assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2));
assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (2, 2));
}
// Send some candidates, check if the candidate won't be found once its relay parent leaves the
// view.
#[test]
fn check_candidate_parent_leaving_view() {
let test_state = TestState::default();
let view = test_harness(|mut virtual_overseer| async move {
// Leaf A
let leaf_a = TestLeaf {
number: 100,
hash: Hash::from_low_u64_be(130),
para_data: vec![
(1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))),
(2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))),
],
};
// Leaf B
let leaf_b = TestLeaf {
number: 101,
hash: Hash::from_low_u64_be(131),
para_data: vec![
(1.into(), PerParaData::new(99, HeadData(vec![3, 4, 5]))),
(2.into(), PerParaData::new(101, HeadData(vec![4, 5, 6]))),
],
};
// Leaf C
let leaf_c = TestLeaf {
number: 102,
hash: Hash::from_low_u64_be(132),
para_data: vec![
(1.into(), PerParaData::new(102, HeadData(vec![5, 6, 7]))),
(2.into(), PerParaData::new(98, HeadData(vec![6, 7, 8]))),
],
};
// Activate leaves.
activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;
activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await;
activate_leaf(&mut virtual_overseer, &leaf_c, &test_state).await;
// Candidate A1
let (candidate_a1, pvd_a1) = make_candidate(
leaf_a.hash,
leaf_a.number,
1.into(),
HeadData(vec![1, 2, 3]),
HeadData(vec![1]),
test_state.validation_code_hash,
);
let candidate_hash_a1 = candidate_a1.hash();
// Candidate A2
let (candidate_a2, pvd_a2) = make_candidate(
leaf_a.hash,
leaf_a.number,
2.into(),
HeadData(vec![2, 3, 4]),
HeadData(vec![2]),
test_state.validation_code_hash,
);
let candidate_hash_a2 = candidate_a2.hash();
// Candidate B
let (candidate_b, pvd_b) = make_candidate(
leaf_b.hash,
leaf_b.number,
1.into(),
HeadData(vec![3, 4, 5]),
HeadData(vec![3]),
test_state.validation_code_hash,
);
let candidate_hash_b = candidate_b.hash();
let response_b = vec![(leaf_b.hash, vec![0])];
// Candidate C
let (candidate_c, pvd_c) = make_candidate(
leaf_c.hash,
leaf_c.number,
2.into(),
HeadData(vec![6, 7, 8]),
HeadData(vec![4]),
test_state.validation_code_hash,
);
let candidate_hash_c = candidate_c.hash();
let response_c = vec![(leaf_c.hash, vec![0])];
// Introduce candidates.
introduce_candidate(&mut virtual_overseer, candidate_a1, pvd_a1).await;
introduce_candidate(&mut virtual_overseer, candidate_a2, pvd_a2).await;
introduce_candidate(&mut virtual_overseer, candidate_b, pvd_b).await;
introduce_candidate(&mut virtual_overseer, candidate_c, pvd_c).await;
// Deactivate leaf A.
deactivate_leaf(&mut virtual_overseer, leaf_a.hash).await;
// Candidates A1 and A2 should be gone. Candidates B and C should remain.
get_membership(&mut virtual_overseer, 1.into(), candidate_hash_a1, vec![]).await;
get_membership(&mut virtual_overseer, 2.into(), candidate_hash_a2, vec![]).await;
get_membership(&mut virtual_overseer, 1.into(), candidate_hash_b, response_b).await;
get_membership(&mut virtual_overseer, 2.into(), candidate_hash_c, response_c.clone()).await;
// Deactivate leaf B.
deactivate_leaf(&mut virtual_overseer, leaf_b.hash).await;
// Candidate B should be gone, C should remain.
get_membership(&mut virtual_overseer, 1.into(), candidate_hash_a1, vec![]).await;
get_membership(&mut virtual_overseer, 2.into(), candidate_hash_a2, vec![]).await;
get_membership(&mut virtual_overseer, 1.into(), candidate_hash_b, vec![]).await;
get_membership(&mut virtual_overseer, 2.into(), candidate_hash_c, response_c).await;
// Deactivate leaf C.
deactivate_leaf(&mut virtual_overseer, leaf_c.hash).await;
// Candidate C should be gone.
get_membership(&mut virtual_overseer, 1.into(), candidate_hash_a1, vec![]).await;
get_membership(&mut virtual_overseer, 2.into(), candidate_hash_a2, vec![]).await;
get_membership(&mut virtual_overseer, 1.into(), candidate_hash_b, vec![]).await;
get_membership(&mut virtual_overseer, 2.into(), candidate_hash_c, vec![]).await;
virtual_overseer
});
assert_eq!(view.active_leaves.len(), 0);
assert_eq!(view.candidate_storage.len(), 0);
}
// Introduce a candidate to multiple forks, see how the membership is returned.
#[test]
fn check_candidate_on_multiple_forks() {
let test_state = TestState::default();
let view = test_harness(|mut virtual_overseer| async move {
// Leaf A
let leaf_a = TestLeaf {
number: 100,
hash: Hash::from_low_u64_be(130),
para_data: vec![
(1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))),
(2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))),
],
};
// Leaf B
let leaf_b = TestLeaf {
number: 101,
hash: Hash::from_low_u64_be(131),
para_data: vec![
(1.into(), PerParaData::new(99, HeadData(vec![3, 4, 5]))),
(2.into(), PerParaData::new(101, HeadData(vec![4, 5, 6]))),
],
};
// Leaf C
let leaf_c = TestLeaf {
number: 102,
hash: Hash::from_low_u64_be(132),
para_data: vec![
(1.into(), PerParaData::new(102, HeadData(vec![5, 6, 7]))),
(2.into(), PerParaData::new(98, HeadData(vec![6, 7, 8]))),
],
};
// Activate leaves.
activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;
activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await;
activate_leaf(&mut virtual_overseer, &leaf_c, &test_state).await;
// Candidate on leaf A.
let (candidate_a, pvd_a) = make_candidate(
leaf_a.hash,
leaf_a.number,
1.into(),
HeadData(vec![1, 2, 3]),
HeadData(vec![1]),
test_state.validation_code_hash,
);
let candidate_hash_a = candidate_a.hash();
let response_a = vec![(leaf_a.hash, vec![0])];
// Candidate on leaf B.
let (candidate_b, pvd_b) = make_candidate(
leaf_b.hash,
leaf_b.number,
1.into(),
HeadData(vec![3, 4, 5]),
HeadData(vec![1]),
test_state.validation_code_hash,
);
let candidate_hash_b = candidate_b.hash();
let response_b = vec![(leaf_b.hash, vec![0])];
// Candidate on leaf C.
let (candidate_c, pvd_c) = make_candidate(
leaf_c.hash,
leaf_c.number,
1.into(),
HeadData(vec![5, 6, 7]),
HeadData(vec![1]),
test_state.validation_code_hash,
);
let candidate_hash_c = candidate_c.hash();
let response_c = vec![(leaf_c.hash, vec![0])];
// Introduce candidates on all three leaves.
introduce_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await;
introduce_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b).await;
introduce_candidate(&mut virtual_overseer, candidate_c.clone(), pvd_c).await;
// Check candidate tree membership.
get_membership(&mut virtual_overseer, 1.into(), candidate_hash_a, response_a).await;
get_membership(&mut virtual_overseer, 1.into(), candidate_hash_b, response_b).await;
get_membership(&mut virtual_overseer, 1.into(), candidate_hash_c, response_c).await;
virtual_overseer
});
assert_eq!(view.active_leaves.len(), 3);
assert_eq!(view.candidate_storage.len(), 2);
// Three parents and three candidates on para 1.
assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (3, 3));
assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0));
}
// Backs some candidates and tests `GetBackableCandidate`.
#[test]
fn check_backable_query() {
let test_state = TestState::default();
let view = test_harness(|mut virtual_overseer| async move {
// Leaf A
let leaf_a = TestLeaf {
number: 100,
hash: Hash::from_low_u64_be(130),
para_data: vec![
(1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))),
(2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))),
],
};
// Activate leaves.
activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;
// Candidate A
let (candidate_a, pvd_a) = make_candidate(
leaf_a.hash,
leaf_a.number,
1.into(),
HeadData(vec![1, 2, 3]),
HeadData(vec![1]),
test_state.validation_code_hash,
);
let candidate_hash_a = candidate_a.hash();
// Candidate B
let (mut candidate_b, pvd_b) = make_candidate(
leaf_a.hash,
leaf_a.number,
1.into(),
HeadData(vec![1]),
HeadData(vec![2]),
test_state.validation_code_hash,
);
// Set a field to make this candidate unique.
candidate_b.descriptor.para_head = Hash::from_low_u64_le(1000);
let candidate_hash_b = candidate_b.hash();
// Introduce candidates.
introduce_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await;
introduce_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b).await;
// Should not get any backable candidates.
get_backable_candidate(
&mut virtual_overseer,
&leaf_a,
1.into(),
vec![candidate_hash_a],
None,
)
.await;
// Second candidates.
second_candidate(&mut virtual_overseer, candidate_a.clone()).await;
second_candidate(&mut virtual_overseer, candidate_b.clone()).await;
// Should not get any backable candidates.
get_backable_candidate(
&mut virtual_overseer,
&leaf_a,
1.into(),
vec![candidate_hash_a],
None,
)
.await;
// Back candidates.
back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await;
back_candidate(&mut virtual_overseer, &candidate_b, candidate_hash_b).await;
// Get backable candidate.
get_backable_candidate(
&mut virtual_overseer,
&leaf_a,
1.into(),
vec![],
Some((candidate_hash_a, leaf_a.hash)),
)
.await;
get_backable_candidate(
&mut virtual_overseer,
&leaf_a,
1.into(),
vec![candidate_hash_a],
Some((candidate_hash_b, leaf_a.hash)),
)
.await;
// Should not get anything at the wrong path.
get_backable_candidate(
&mut virtual_overseer,
&leaf_a,
1.into(),
vec![candidate_hash_b],
None,
)
.await;
virtual_overseer
});
assert_eq!(view.active_leaves.len(), 1);
assert_eq!(view.candidate_storage.len(), 2);
// Two parents and two candidates on para 1.
assert_eq!(view.candidate_storage.get(&1.into()).unwrap().len(), (2, 2));
assert_eq!(view.candidate_storage.get(&2.into()).unwrap().len(), (0, 0));
}
// Test depth query.
#[test]
fn check_hypothetical_frontier_query() {
let test_state = TestState::default();
let view = test_harness(|mut virtual_overseer| async move {
// Leaf A
let leaf_a = TestLeaf {
number: 100,
hash: Hash::from_low_u64_be(130),
para_data: vec![
(1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))),
(2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))),
],
};
// Activate leaves.
activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;
// Candidate A.
let (candidate_a, pvd_a) = make_candidate(
leaf_a.hash,
leaf_a.number,
1.into(),
HeadData(vec![1, 2, 3]),
HeadData(vec![1]),
test_state.validation_code_hash,
);
let candidate_hash_a = candidate_a.hash();
// Candidate B.
let (candidate_b, pvd_b) = make_candidate(
leaf_a.hash,
leaf_a.number,
1.into(),
HeadData(vec![1]),
HeadData(vec![2]),
test_state.validation_code_hash,
);
let candidate_hash_b = candidate_b.hash();
// Candidate C.
let (candidate_c, pvd_c) = make_candidate(
leaf_a.hash,
leaf_a.number,
1.into(),
HeadData(vec![2]),
HeadData(vec![3]),
test_state.validation_code_hash,
);
let candidate_hash_c = candidate_c.hash();
// Get hypothetical frontier of candidate A before adding it.
get_hypothetical_frontier(
&mut virtual_overseer,
candidate_hash_a,
candidate_a.clone(),
pvd_a.clone(),
leaf_a.hash,
false,
vec![0],
)
.await;
// Should work with `backed_in_path_only: true`, too.
get_hypothetical_frontier(
&mut virtual_overseer,
candidate_hash_a,
candidate_a.clone(),
pvd_a.clone(),
leaf_a.hash,
true,
vec![0],
)
.await;
// Add candidate A.
introduce_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()).await;
// Get frontier of candidate A after adding it.
get_hypothetical_frontier(
&mut virtual_overseer,
candidate_hash_a,
candidate_a.clone(),
pvd_a.clone(),
leaf_a.hash,
false,
vec![0],
)
.await;
// Get hypothetical frontier of candidate B before adding it.
get_hypothetical_frontier(
&mut virtual_overseer,
candidate_hash_b,
candidate_b.clone(),
pvd_b.clone(),
leaf_a.hash,
false,
vec![1],
)
.await;
// Add candidate B.
introduce_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b.clone()).await;
// Get frontier of candidate B after adding it.
get_hypothetical_frontier(
&mut virtual_overseer,
candidate_hash_b,
candidate_b,
pvd_b.clone(),
leaf_a.hash,
false,
vec![1],
)
.await;
// Get hypothetical frontier of candidate C before adding it.
get_hypothetical_frontier(
&mut virtual_overseer,
candidate_hash_c,
candidate_c.clone(),
pvd_c.clone(),
leaf_a.hash,
false,
vec![2],
)
.await;
// Should be empty with `backed_in_path_only` because we haven't backed anything.
get_hypothetical_frontier(
&mut virtual_overseer,
candidate_hash_c,
candidate_c.clone(),
pvd_c.clone(),
leaf_a.hash,
true,
vec![],
)
.await;
// Add candidate C.
introduce_candidate(&mut virtual_overseer, candidate_c.clone(), pvd_c.clone()).await;
// Get frontier of candidate C after adding it.
get_hypothetical_frontier(
&mut virtual_overseer,
candidate_hash_c,
candidate_c.clone(),
pvd_c.clone(),
leaf_a.hash,
false,
vec![2],
)
.await;
// Should be empty with `backed_in_path_only` because we haven't backed anything.
get_hypothetical_frontier(
&mut virtual_overseer,
candidate_hash_c,
candidate_c.clone(),
pvd_c.clone(),
leaf_a.hash,
true,
vec![],
)
.await;
virtual_overseer
});
assert_eq!(view.active_leaves.len(), 1);
assert_eq!(view.candidate_storage.len(), 2);
}
#[test]
fn check_pvd_query() {
let test_state = TestState::default();
let view = test_harness(|mut virtual_overseer| async move {
// Leaf A
let leaf_a = TestLeaf {
number: 100,
hash: Hash::from_low_u64_be(130),
para_data: vec![
(1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))),
(2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))),
],
};
// Activate leaves.
activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;
// Candidate A.
let (candidate_a, pvd_a) = make_candidate(
leaf_a.hash,
leaf_a.number,
1.into(),
HeadData(vec![1, 2, 3]),
HeadData(vec![1]),
test_state.validation_code_hash,
);
// Candidate B.
let (candidate_b, pvd_b) = make_candidate(
leaf_a.hash,
leaf_a.number,
1.into(),
HeadData(vec![1]),
HeadData(vec![2]),
test_state.validation_code_hash,
);
// Candidate C.
let (candidate_c, pvd_c) = make_candidate(
leaf_a.hash,
leaf_a.number,
1.into(),
HeadData(vec![2]),
HeadData(vec![3]),
test_state.validation_code_hash,
);
// Get pvd of candidate A before adding it.
get_pvd(
&mut virtual_overseer,
1.into(),
leaf_a.hash,
HeadData(vec![1, 2, 3]),
Some(pvd_a.clone()),
)
.await;
// Add candidate A.
introduce_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a.clone()).await;
back_candidate(&mut virtual_overseer, &candidate_a, candidate_a.hash()).await;
// Get pvd of candidate A after adding it.
get_pvd(
&mut virtual_overseer,
1.into(),
leaf_a.hash,
HeadData(vec![1, 2, 3]),
Some(pvd_a.clone()),
)
.await;
// Get pvd of candidate B before adding it.
get_pvd(
&mut virtual_overseer,
1.into(),
leaf_a.hash,
HeadData(vec![1]),
Some(pvd_b.clone()),
)
.await;
// Add candidate B.
introduce_candidate(&mut virtual_overseer, candidate_b, pvd_b.clone()).await;
// Get pvd of candidate B after adding it.
get_pvd(
&mut virtual_overseer,
1.into(),
leaf_a.hash,
HeadData(vec![1]),
Some(pvd_b.clone()),
)
.await;
// Get pvd of candidate C before adding it.
get_pvd(
&mut virtual_overseer,
1.into(),
leaf_a.hash,
HeadData(vec![2]),
Some(pvd_c.clone()),
)
.await;
// Add candidate C.
introduce_candidate(&mut virtual_overseer, candidate_c, pvd_c.clone()).await;
// Get pvd of candidate C after adding it.
get_pvd(
&mut virtual_overseer,
1.into(),
leaf_a.hash,
HeadData(vec![2]),
Some(pvd_c.clone()),
)
.await;
virtual_overseer
});
assert_eq!(view.active_leaves.len(), 1);
assert_eq!(view.candidate_storage.len(), 2);
}
// Test simultaneously activating and deactivating leaves, and simultaneously deactivating multiple
// leaves.
#[test]
fn correctly_updates_leaves() {
let test_state = TestState::default();
let view = test_harness(|mut virtual_overseer| async move {
// Leaf A
let leaf_a = TestLeaf {
number: 100,
hash: Hash::from_low_u64_be(130),
para_data: vec![
(1.into(), PerParaData::new(97, HeadData(vec![1, 2, 3]))),
(2.into(), PerParaData::new(100, HeadData(vec![2, 3, 4]))),
],
};
// Leaf B
let leaf_b = TestLeaf {
number: 101,
hash: Hash::from_low_u64_be(131),
para_data: vec![
(1.into(), PerParaData::new(99, HeadData(vec![3, 4, 5]))),
(2.into(), PerParaData::new(101, HeadData(vec![4, 5, 6]))),
],
};
// Leaf C
let leaf_c = TestLeaf {
number: 102,
hash: Hash::from_low_u64_be(132),
para_data: vec![
(1.into(), PerParaData::new(102, HeadData(vec![5, 6, 7]))),
(2.into(), PerParaData::new(98, HeadData(vec![6, 7, 8]))),
],
};
// Activate leaves.
activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;
activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await;
// Try activating a duplicate leaf.
activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await;
// Pass in an empty update.
let update = ActiveLeavesUpdate::default();
virtual_overseer
.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)))
.await;
// Activate a leaf and remove one at the same time.
let activated = new_leaf(leaf_c.hash, leaf_c.number);
let update = ActiveLeavesUpdate {
activated: Some(activated),
deactivated: [leaf_b.hash][..].into(),
};
virtual_overseer
.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)))
.await;
handle_leaf_activation(
&mut virtual_overseer,
&leaf_c,
&test_state,
ASYNC_BACKING_PARAMETERS,
)
.await;
// Remove all remaining leaves.
let update = ActiveLeavesUpdate {
deactivated: [leaf_a.hash, leaf_c.hash][..].into(),
..Default::default()
};
virtual_overseer
.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)))
.await;
// Activate and deactivate the same leaf.
let activated = new_leaf(leaf_a.hash, leaf_a.number);
let update = ActiveLeavesUpdate {
activated: Some(activated),
deactivated: [leaf_a.hash][..].into(),
};
virtual_overseer
.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)))
.await;
handle_leaf_activation(
&mut virtual_overseer,
&leaf_a,
&test_state,
ASYNC_BACKING_PARAMETERS,
)
.await;
// Remove the leaf again. Send some unnecessary hashes.
let update = ActiveLeavesUpdate {
deactivated: [leaf_a.hash, leaf_b.hash, leaf_c.hash][..].into(),
..Default::default()
};
virtual_overseer
.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)))
.await;
virtual_overseer
});
assert_eq!(view.active_leaves.len(), 0);
assert_eq!(view.candidate_storage.len(), 0);
}
#[test]
fn persists_pending_availability_candidate() {
let mut test_state = TestState::default();
let para_id = ParaId::from(1);
test_state.availability_cores = test_state
.availability_cores
.into_iter()
.filter(|core| core.para_id().map_or(false, |id| id == para_id))
.collect();
assert_eq!(test_state.availability_cores.len(), 1);
test_harness(|mut virtual_overseer| async move {
let para_head = HeadData(vec![1, 2, 3]);
// Min allowed relay parent for leaf `a` which goes out of scope in the test.
let candidate_relay_parent = Hash::from_low_u64_be(5);
let candidate_relay_parent_number = 97;
let leaf_a = TestLeaf {
number: candidate_relay_parent_number + ALLOWED_ANCESTRY_LEN,
hash: Hash::from_low_u64_be(2),
para_data: vec![(
para_id,
PerParaData::new(candidate_relay_parent_number, para_head.clone()),
)],
};
let leaf_b_hash = Hash::from_low_u64_be(1);
let leaf_b_number = leaf_a.number + 1;
// Activate leaf.
activate_leaf(&mut virtual_overseer, &leaf_a, &test_state).await;
// Candidate A
let (candidate_a, pvd_a) = make_candidate(
candidate_relay_parent,
candidate_relay_parent_number,
para_id,
para_head.clone(),
HeadData(vec![1]),
test_state.validation_code_hash,
);
let candidate_hash_a = candidate_a.hash();
// Candidate B, built on top of the candidate which is out of scope but pending
// availability.
let (candidate_b, pvd_b) = make_candidate(
leaf_b_hash,
leaf_b_number,
para_id,
HeadData(vec![1]),
HeadData(vec![2]),
test_state.validation_code_hash,
);
let candidate_hash_b = candidate_b.hash();
introduce_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await;
second_candidate(&mut virtual_overseer, candidate_a.clone()).await;
back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await;
let candidate_a_pending_av = CandidatePendingAvailability {
candidate_hash: candidate_hash_a,
descriptor: candidate_a.descriptor.clone(),
commitments: candidate_a.commitments.clone(),
relay_parent_number: candidate_relay_parent_number,
max_pov_size: MAX_POV_SIZE,
};
let leaf_b = TestLeaf {
number: leaf_b_number,
hash: leaf_b_hash,
para_data: vec![(
1.into(),
PerParaData::new_with_pending(
candidate_relay_parent_number + 1,
para_head.clone(),
vec![candidate_a_pending_av],
),
)],
};
activate_leaf(&mut virtual_overseer, &leaf_b, &test_state).await;
introduce_candidate(&mut virtual_overseer, candidate_b.clone(), pvd_b).await;
second_candidate(&mut virtual_overseer, candidate_b.clone()).await;
back_candidate(&mut virtual_overseer, &candidate_b, candidate_hash_b).await;
get_backable_candidate(
&mut virtual_overseer,
&leaf_b,
para_id,
vec![candidate_hash_a],
Some((candidate_hash_b, leaf_b_hash)),
)
.await;
virtual_overseer
});
}
#[test]
fn backwards_compatible() {
let mut test_state = TestState::default();
let para_id = ParaId::from(1);
test_state.availability_cores = test_state
.availability_cores
.into_iter()
.filter(|core| core.para_id().map_or(false, |id| id == para_id))
.collect();
assert_eq!(test_state.availability_cores.len(), 1);
test_harness(|mut virtual_overseer| async move {
let para_head = HeadData(vec![1, 2, 3]);
let leaf_b_hash = Hash::repeat_byte(15);
let candidate_relay_parent = get_parent_hash(leaf_b_hash);
let candidate_relay_parent_number = 100;
let leaf_a = TestLeaf {
number: candidate_relay_parent_number,
hash: candidate_relay_parent,
para_data: vec![(
para_id,
PerParaData::new(candidate_relay_parent_number, para_head.clone()),
)],
};
// Activate leaf.
activate_leaf_with_params(
&mut virtual_overseer,
&leaf_a,
&test_state,
AsyncBackingParams { allowed_ancestry_len: 0, max_candidate_depth: 0 },
)
.await;
// Candidate A
let (candidate_a, pvd_a) = make_candidate(
candidate_relay_parent,
candidate_relay_parent_number,
para_id,
para_head.clone(),
HeadData(vec![1]),
test_state.validation_code_hash,
);
let candidate_hash_a = candidate_a.hash();
introduce_candidate(&mut virtual_overseer, candidate_a.clone(), pvd_a).await;
second_candidate(&mut virtual_overseer, candidate_a.clone()).await;
back_candidate(&mut virtual_overseer, &candidate_a, candidate_hash_a).await;
get_backable_candidate(
&mut virtual_overseer,
&leaf_a,
para_id,
vec![],
Some((candidate_hash_a, candidate_relay_parent)),
)
.await;
let leaf_b = TestLeaf {
number: candidate_relay_parent_number + 1,
hash: leaf_b_hash,
para_data: vec![(
para_id,
PerParaData::new(candidate_relay_parent_number + 1, para_head.clone()),
)],
};
activate_leaf_with_params(
&mut virtual_overseer,
&leaf_b,
&test_state,
AsyncBackingParams { allowed_ancestry_len: 0, max_candidate_depth: 0 },
)
.await;
get_backable_candidate(&mut virtual_overseer, &leaf_b, para_id, vec![], None).await;
virtual_overseer
});
}
#[test]
fn uses_ancestry_only_within_session() {
test_harness(|mut virtual_overseer| async move {
let number = 5;
let hash = Hash::repeat_byte(5);
let ancestry_len = 3;
let session = 2;
let ancestry_hashes =
vec![Hash::repeat_byte(4), Hash::repeat_byte(3), Hash::repeat_byte(2)];
let session_change_hash = Hash::repeat_byte(3);
let activated = new_leaf(hash, number);
virtual_overseer
.send(FromOrchestra::Signal(OverseerSignal::ActiveLeaves(
ActiveLeavesUpdate::start_work(activated),
)))
.await;
assert_matches!(
virtual_overseer.recv().await,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(parent, RuntimeApiRequest::AsyncBackingParams(tx))
) if parent == hash => {
tx.send(Ok(AsyncBackingParams { max_candidate_depth: 0, allowed_ancestry_len: ancestry_len })).unwrap();
}
);
assert_matches!(
virtual_overseer.recv().await,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(parent, RuntimeApiRequest::AvailabilityCores(tx))
) if parent == hash => {
tx.send(Ok(Vec::new())).unwrap();
}
);
send_block_header(&mut virtual_overseer, hash, number).await;
assert_matches!(
virtual_overseer.recv().await,
AllMessages::ChainApi(
ChainApiMessage::Ancestors{hash: block_hash, k, response_channel: tx}
) if block_hash == hash && k == ancestry_len as usize => {
tx.send(Ok(ancestry_hashes.clone())).unwrap();
}
);
assert_matches!(
virtual_overseer.recv().await,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(parent, RuntimeApiRequest::SessionIndexForChild(tx))
) if parent == hash => {
tx.send(Ok(session)).unwrap();
}
);
for (i, hash) in ancestry_hashes.into_iter().enumerate() {
let number = number - (i + 1) as BlockNumber;
send_block_header(&mut virtual_overseer, hash, number).await;
assert_matches!(
virtual_overseer.recv().await,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(parent, RuntimeApiRequest::SessionIndexForChild(tx))
) if parent == hash => {
if hash == session_change_hash {
tx.send(Ok(session - 1)).unwrap();
break
} else {
tx.send(Ok(session)).unwrap();
}
}
);
}
virtual_overseer
});
}