// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // This file is part of Pezkuwi. // Pezkuwi 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. // Pezkuwi 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 Pezkuwi. If not, see . use super::*; use pezkuwi_node_subsystem::SpawnGlue; use pezkuwi_node_subsystem_test_helpers::make_subsystem_context; use pezkuwi_pez_node_primitives::{BabeAllowedSlots, BabeEpoch, BabeEpochConfiguration}; use pezkuwi_primitives::{ async_backing, async_backing::Constraints, slashing, ApprovalVotingParams, AuthorityDiscoveryId, BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, Slot, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; use pezkuwi_primitives_test_helpers::{ dummy_committed_candidate_receipt_v2, dummy_validation_code, }; use pezsp_api::ApiError; use pezsp_core::testing::TaskExecutor; use std::{ collections::{BTreeMap, HashMap, VecDeque}, sync::{Arc, Mutex}, }; #[derive(Default)] struct MockSubsystemClient { submitted_pvf_check_statement: Arc>>, authorities: Vec, validators: Vec, validator_groups: Vec>, availability_cores: Vec, validation_data: HashMap, validation_code: HashMap, validation_outputs_results: HashMap, session_index_for_child: SessionIndex, candidate_pending_availability: HashMap, candidates_pending_availability: HashMap>, dmq_contents: HashMap>, hrmp_channels: HashMap>>, validation_code_by_hash: HashMap, availability_cores_wait: Arc>, babe_epoch: Option, pvfs_require_precheck: Vec, validation_code_hash: HashMap, session_info: HashMap, candidate_events: Vec, } #[async_trait::async_trait] impl RuntimeApiSubsystemClient for MockSubsystemClient { async fn api_version_teyrchain_host(&self, _: Hash) -> Result, ApiError> { Ok(Some(5)) } async fn validators(&self, _: Hash) -> Result, ApiError> { Ok(self.validators.clone()) } async fn validator_groups( &self, _: Hash, ) -> Result<(Vec>, GroupRotationInfo), ApiError> { Ok(( self.validator_groups.clone(), GroupRotationInfo { session_start_block: 1, group_rotation_frequency: 100, now: 10 }, )) } async fn availability_cores( &self, _: Hash, ) -> Result>, ApiError> { let _lock = self.availability_cores_wait.lock().unwrap(); Ok(self.availability_cores.clone()) } async fn persisted_validation_data( &self, _: Hash, para_id: ParaId, _: OccupiedCoreAssumption, ) -> Result>, ApiError> { Ok(self.validation_data.get(¶_id).cloned()) } async fn assumed_validation_data( &self, _: Hash, para_id: ParaId, expected_persisted_validation_data_hash: Hash, ) -> Result, ValidationCodeHash)>, ApiError> { Ok(self .validation_data .get(¶_id) .cloned() .filter(|data| data.hash() == expected_persisted_validation_data_hash) .zip(self.validation_code.get(¶_id).map(|code| code.hash()))) } async fn check_validation_outputs( &self, _: Hash, para_id: ParaId, _: CandidateCommitments, ) -> Result { Ok(self.validation_outputs_results.get(¶_id).copied().unwrap()) } async fn session_index_for_child(&self, _: Hash) -> Result { Ok(self.session_index_for_child) } async fn validation_code( &self, _: Hash, para_id: ParaId, _: OccupiedCoreAssumption, ) -> Result, ApiError> { Ok(self.validation_code.get(¶_id).cloned()) } async fn candidate_pending_availability( &self, _: Hash, para_id: ParaId, ) -> Result>, ApiError> { Ok(self.candidate_pending_availability.get(¶_id).cloned()) } async fn candidates_pending_availability( &self, _: Hash, para_id: ParaId, ) -> Result>, ApiError> { Ok(self.candidates_pending_availability.get(¶_id).cloned().unwrap_or_default()) } async fn candidate_events(&self, _: Hash) -> Result>, ApiError> { Ok(self.candidate_events.clone()) } async fn dmq_contents( &self, _: Hash, para_id: ParaId, ) -> Result>, ApiError> { Ok(self.dmq_contents.get(¶_id).cloned().unwrap()) } async fn inbound_hrmp_channels_contents( &self, _: Hash, para_id: ParaId, ) -> Result>>, ApiError> { Ok(self.hrmp_channels.get(¶_id).cloned().unwrap()) } async fn validation_code_by_hash( &self, _: Hash, hash: ValidationCodeHash, ) -> Result, ApiError> { Ok(self.validation_code_by_hash.get(&hash).cloned()) } async fn on_chain_votes(&self, _: Hash) -> Result>, ApiError> { todo!("Not required for tests") } async fn session_info( &self, _: Hash, index: SessionIndex, ) -> Result, ApiError> { Ok(self.session_info.get(&index).cloned()) } async fn submit_pvf_check_statement( &self, _: Hash, stmt: PvfCheckStatement, sig: ValidatorSignature, ) -> Result<(), ApiError> { self.submitted_pvf_check_statement.lock().unwrap().push((stmt, sig)); Ok(()) } async fn pvfs_require_precheck(&self, _: Hash) -> Result, ApiError> { Ok(self.pvfs_require_precheck.clone()) } async fn validation_code_hash( &self, _: Hash, para_id: ParaId, _: OccupiedCoreAssumption, ) -> Result, ApiError> { Ok(self.validation_code_hash.get(¶_id).cloned()) } async fn disputes( &self, _: Hash, ) -> Result)>, ApiError> { todo!("Not required for tests") } async fn unapplied_slashes( &self, _: Hash, ) -> Result, ApiError> { todo!("Not required for tests") } async fn unapplied_slashes_v2( &self, _: Hash, ) -> Result, ApiError> { todo!("Not required for tests") } async fn key_ownership_proof( &self, _: Hash, _: ValidatorId, ) -> Result, ApiError> { todo!("Not required for tests") } async fn submit_report_dispute_lost( &self, _: Hash, _: slashing::DisputeProof, _: slashing::OpaqueKeyOwnershipProof, ) -> Result, ApiError> { todo!("Not required for tests") } async fn session_executor_params( &self, _: Hash, _: SessionIndex, ) -> Result, ApiError> { todo!("Not required for tests") } /// Approval voting configuration parameters async fn approval_voting_params( &self, _: Hash, _: SessionIndex, ) -> Result { todo!("Not required for tests") } async fn current_epoch(&self, _: Hash) -> Result { Ok(self.babe_epoch.as_ref().unwrap().clone()) } async fn authorities(&self, _: Hash) -> Result, ApiError> { Ok(self.authorities.clone()) } async fn async_backing_params( &self, _: Hash, ) -> Result { todo!("Not required for tests") } async fn para_backing_state( &self, _: Hash, _: ParaId, ) -> Result, ApiError> { todo!("Not required for tests") } async fn minimum_backing_votes(&self, _: Hash, _: SessionIndex) -> Result { todo!("Not required for tests") } async fn node_features(&self, _: Hash) -> Result { todo!("Not required for tests") } async fn disabled_validators(&self, _: Hash) -> Result, ApiError> { todo!("Not required for tests") } async fn claim_queue( &self, _: Hash, ) -> Result>, ApiError> { todo!("Not required for tests") } async fn scheduling_lookahead(&self, _: Hash) -> Result { todo!("Not required for tests") } async fn backing_constraints( &self, _at: Hash, _para_id: ParaId, ) -> Result, ApiError> { todo!("Not required for tests") } async fn validation_code_bomb_limit(&self, _: Hash) -> Result { todo!("Not required for tests") } async fn para_ids(&self, _: Hash) -> Result, ApiError> { todo!("Not required for tests") } } #[test] fn requests_authorities() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let subsystem_client = Arc::new(MockSubsystemClient::default()); let relay_parent = [1; 32].into(); let spawner = pezsp_core::testing::TaskExecutor::new(); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request(relay_parent, Request::Authorities(tx)), }) .await; assert_eq!(rx.await.unwrap().unwrap(), subsystem_client.authorities); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_validators() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let subsystem_client = Arc::new(MockSubsystemClient::default()); let relay_parent = [1; 32].into(); let spawner = pezsp_core::testing::TaskExecutor::new(); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request(relay_parent, Request::Validators(tx)), }) .await; assert_eq!(rx.await.unwrap().unwrap(), subsystem_client.validators); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_validator_groups() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let subsystem_client = Arc::new(MockSubsystemClient::default()); let relay_parent = [1; 32].into(); let spawner = pezsp_core::testing::TaskExecutor::new(); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request(relay_parent, Request::ValidatorGroups(tx)), }) .await; assert_eq!(rx.await.unwrap().unwrap().0, subsystem_client.validator_groups); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_availability_cores() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let subsystem_client = Arc::new(MockSubsystemClient::default()); let relay_parent = [1; 32].into(); let spawner = pezsp_core::testing::TaskExecutor::new(); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request(relay_parent, Request::AvailabilityCores(tx)), }) .await; assert_eq!(rx.await.unwrap().unwrap(), subsystem_client.availability_cores); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_persisted_validation_data() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let relay_parent = [1; 32].into(); let para_a = ParaId::from(5_u32); let para_b = ParaId::from(6_u32); let spawner = pezsp_core::testing::TaskExecutor::new(); let mut subsystem_client = MockSubsystemClient::default(); subsystem_client.validation_data.insert(para_a, Default::default()); let subsystem_client = Arc::new(subsystem_client); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::PersistedValidationData(para_a, OccupiedCoreAssumption::Included, tx), ), }) .await; assert_eq!(rx.await.unwrap().unwrap(), Some(Default::default())); let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::PersistedValidationData(para_b, OccupiedCoreAssumption::Included, tx), ), }) .await; assert_eq!(rx.await.unwrap().unwrap(), None); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_assumed_validation_data() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let relay_parent = [1; 32].into(); let para_a = ParaId::from(5_u32); let para_b = ParaId::from(6_u32); let spawner = pezsp_core::testing::TaskExecutor::new(); let validation_code = ValidationCode(vec![1, 2, 3]); let expected_data_hash = ::default().hash(); let expected_code_hash = validation_code.hash(); let mut subsystem_client = MockSubsystemClient::default(); subsystem_client.validation_data.insert(para_a, Default::default()); subsystem_client.validation_code.insert(para_a, validation_code); subsystem_client.validation_data.insert(para_b, Default::default()); let subsystem_client = Arc::new(subsystem_client); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::AssumedValidationData(para_a, expected_data_hash, tx), ), }) .await; assert_eq!(rx.await.unwrap().unwrap(), Some((Default::default(), expected_code_hash))); let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::AssumedValidationData(para_a, Hash::zero(), tx), ), }) .await; assert_eq!(rx.await.unwrap().unwrap(), None); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_check_validation_outputs() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let mut subsystem_client = MockSubsystemClient::default(); let relay_parent = [1; 32].into(); let para_a = ParaId::from(5_u32); let para_b = ParaId::from(6_u32); let commitments = pezkuwi_primitives::CandidateCommitments::default(); let spawner = pezsp_core::testing::TaskExecutor::new(); subsystem_client.validation_outputs_results.insert(para_a, false); subsystem_client.validation_outputs_results.insert(para_b, true); let subsystem_client = Arc::new(subsystem_client); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::CheckValidationOutputs(para_a, commitments.clone(), tx), ), }) .await; assert_eq!( rx.await.unwrap().unwrap(), subsystem_client.validation_outputs_results[¶_a] ); let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::CheckValidationOutputs(para_b, commitments, tx), ), }) .await; assert_eq!( rx.await.unwrap().unwrap(), subsystem_client.validation_outputs_results[¶_b] ); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_session_index_for_child() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let subsystem_client = Arc::new(MockSubsystemClient::default()); let relay_parent = [1; 32].into(); let spawner = pezsp_core::testing::TaskExecutor::new(); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request(relay_parent, Request::SessionIndexForChild(tx)), }) .await; assert_eq!(rx.await.unwrap().unwrap(), subsystem_client.session_index_for_child); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } fn dummy_session_info() -> SessionInfo { SessionInfo { validators: Default::default(), discovery_keys: vec![], assignment_keys: vec![], validator_groups: Default::default(), n_cores: 4u32, zeroth_delay_tranche_width: 0u32, relay_vrf_modulo_samples: 0u32, n_delay_tranches: 2u32, no_show_slots: 0u32, needed_approvals: 1u32, active_validator_indices: vec![], dispute_period: 6, random_seed: [0u8; 32], } } #[test] fn requests_session_info() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let mut subsystem_client = MockSubsystemClient::default(); let session_index = 1; subsystem_client.session_info.insert(session_index, dummy_session_info()); let subsystem_client = Arc::new(subsystem_client); let spawner = pezsp_core::testing::TaskExecutor::new(); let relay_parent = [1; 32].into(); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::SessionInfo(session_index, tx), ), }) .await; assert_eq!(rx.await.unwrap().unwrap(), Some(dummy_session_info())); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_validation_code() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let relay_parent = [1; 32].into(); let para_a = ParaId::from(5_u32); let para_b = ParaId::from(6_u32); let spawner = pezsp_core::testing::TaskExecutor::new(); let validation_code = dummy_validation_code(); let mut subsystem_client = MockSubsystemClient::default(); subsystem_client.validation_code.insert(para_a, validation_code.clone()); let subsystem_client = Arc::new(subsystem_client); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::ValidationCode(para_a, OccupiedCoreAssumption::Included, tx), ), }) .await; assert_eq!(rx.await.unwrap().unwrap(), Some(validation_code)); let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::ValidationCode(para_b, OccupiedCoreAssumption::Included, tx), ), }) .await; assert_eq!(rx.await.unwrap().unwrap(), None); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_candidate_pending_availability() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let relay_parent = [1; 32].into(); let para_a = ParaId::from(5_u32); let para_b = ParaId::from(6_u32); let spawner = pezsp_core::testing::TaskExecutor::new(); let candidate_receipt = dummy_committed_candidate_receipt_v2(relay_parent); let mut subsystem_client = MockSubsystemClient::default(); subsystem_client .candidate_pending_availability .insert(para_a, candidate_receipt.clone()); let subsystem_client = Arc::new(subsystem_client); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::CandidatePendingAvailability(para_a, tx), ), }) .await; assert_eq!(rx.await.unwrap().unwrap(), Some(candidate_receipt)); let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::CandidatePendingAvailability(para_b, tx), ), }) .await; assert_eq!(rx.await.unwrap().unwrap(), None); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_candidate_events() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let subsystem_client = Arc::new(MockSubsystemClient::default()); let relay_parent = [1; 32].into(); let spawner = pezsp_core::testing::TaskExecutor::new(); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request(relay_parent, Request::CandidateEvents(tx)), }) .await; assert_eq!(rx.await.unwrap().unwrap(), subsystem_client.candidate_events); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_dmq_contents() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let relay_parent = [1; 32].into(); let para_a = ParaId::from(5_u32); let para_b = ParaId::from(6_u32); let spawner = pezsp_core::testing::TaskExecutor::new(); let subsystem_client = Arc::new({ let mut subsystem_client = MockSubsystemClient::default(); subsystem_client.dmq_contents.insert(para_a, vec![]); subsystem_client.dmq_contents.insert( para_b, vec![InboundDownwardMessage { sent_at: 228, msg: b"Novus Ordo Seclorum".to_vec() }], ); subsystem_client }); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request(relay_parent, Request::DmqContents(para_a, tx)), }) .await; assert_eq!(rx.await.unwrap().unwrap(), vec![]); let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request(relay_parent, Request::DmqContents(para_b, tx)), }) .await; assert_eq!( rx.await.unwrap().unwrap(), vec![InboundDownwardMessage { sent_at: 228, msg: b"Novus Ordo Seclorum".to_vec() }] ); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_inbound_hrmp_channels_contents() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let relay_parent = [1; 32].into(); let para_a = ParaId::from(99_u32); let para_b = ParaId::from(66_u32); let para_c = ParaId::from(33_u32); let spawner = pezsp_core::testing::TaskExecutor::new(); let para_b_inbound_channels = [ (para_a, vec![]), (para_c, vec![InboundHrmpMessage { sent_at: 1, data: "๐™€=๐™ˆ๐˜พยฒ".as_bytes().to_owned() }]), ] .into_iter() .collect::>(); let subsystem_client = Arc::new({ let mut subsystem_client = MockSubsystemClient::default(); subsystem_client.hrmp_channels.insert(para_a, BTreeMap::new()); subsystem_client.hrmp_channels.insert(para_b, para_b_inbound_channels.clone()); subsystem_client }); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::InboundHrmpChannelsContents(para_a, tx), ), }) .await; assert_eq!(rx.await.unwrap().unwrap(), BTreeMap::new()); let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::InboundHrmpChannelsContents(para_b, tx), ), }) .await; assert_eq!(rx.await.unwrap().unwrap(), para_b_inbound_channels); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_validation_code_by_hash() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let spawner = pezsp_core::testing::TaskExecutor::new(); let (subsystem_client, validation_code) = { let mut subsystem_client = MockSubsystemClient::default(); let mut validation_code = Vec::new(); for n in 0..5 { let code = ValidationCode::from(vec![n; 32]); subsystem_client.validation_code_by_hash.insert(code.hash(), code.clone()); validation_code.push(code); } (Arc::new(subsystem_client), validation_code) }; let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let relay_parent = [1; 32].into(); let test_task = async move { for code in validation_code { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::ValidationCodeByHash(code.hash(), tx), ), }) .await; assert_eq!(rx.await.unwrap().unwrap(), Some(code)); } ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn multiple_requests_in_parallel_are_working() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let subsystem_client = Arc::new(MockSubsystemClient::default()); let relay_parent = [1; 32].into(); let spawner = pezsp_core::testing::TaskExecutor::new(); let mutex = subsystem_client.availability_cores_wait.clone(); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { // Make all requests block until we release this mutex. let lock = mutex.lock().unwrap(); let mut receivers = Vec::new(); for _ in 0..MAX_PARALLEL_REQUESTS { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request(relay_parent, Request::AvailabilityCores(tx)), }) .await; receivers.push(rx); } // The backpressure from reaching `MAX_PARALLEL_REQUESTS` will make the test block, we need // to drop the lock. drop(lock); for _ in 0..MAX_PARALLEL_REQUESTS * 100 { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request(relay_parent, Request::AvailabilityCores(tx)), }) .await; receivers.push(rx); } let join = future::join_all(receivers); join.await .into_iter() .for_each(|r| assert_eq!(r.unwrap().unwrap(), subsystem_client.availability_cores)); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_babe_epoch() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let mut subsystem_client = MockSubsystemClient::default(); let epoch = BabeEpoch { epoch_index: 100, start_slot: Slot::from(1000), duration: 10, authorities: Vec::new(), randomness: [1u8; 32], config: BabeEpochConfiguration { c: (1, 4), allowed_slots: BabeAllowedSlots::PrimarySlots }, }; subsystem_client.babe_epoch = Some(epoch.clone()); let subsystem_client = Arc::new(subsystem_client); let relay_parent = [1; 32].into(); let spawner = pezsp_core::testing::TaskExecutor::new(); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request(relay_parent, Request::CurrentBabeEpoch(tx)), }) .await; assert_eq!(rx.await.unwrap().unwrap(), epoch); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_submit_pvf_check_statement() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let spawner = pezsp_core::testing::TaskExecutor::new(); let subsystem_client = Arc::new(MockSubsystemClient::default()); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let relay_parent = [1; 32].into(); let test_task = async move { let (stmt, sig) = fake_statement(); // Send the same statement twice. // // Here we just want to ensure that those requests do not go through the cache. let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::SubmitPvfCheckStatement(stmt.clone(), sig.clone(), tx), ), }) .await; let _ = rx.await.unwrap().unwrap(); let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::SubmitPvfCheckStatement(stmt.clone(), sig.clone(), tx), ), }) .await; let _ = rx.await.unwrap().unwrap(); assert_eq!( &*subsystem_client.submitted_pvf_check_statement.lock().expect("poisoned mutex"), &[(stmt.clone(), sig.clone()), (stmt.clone(), sig.clone())] ); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); fn fake_statement() -> (PvfCheckStatement, ValidatorSignature) { let stmt = PvfCheckStatement { accept: true, subject: [1; 32].into(), session_index: 1, validator_index: 1.into(), }; let sig = pezsp_keyring::Sr25519Keyring::Alice.sign(&stmt.signing_payload()).into(); (stmt, sig) } } #[test] fn requests_pvfs_require_precheck() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let spawner = pezsp_core::testing::TaskExecutor::new(); let subsystem_client = Arc::new({ let mut subsystem_client = MockSubsystemClient::default(); subsystem_client.pvfs_require_precheck = vec![[1; 32].into(), [2; 32].into()]; subsystem_client }); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let relay_parent = [1; 32].into(); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request(relay_parent, Request::PvfsRequirePrecheck(tx)), }) .await; assert_eq!(rx.await.unwrap().unwrap(), vec![[1; 32].into(), [2; 32].into()]); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); } #[test] fn requests_validation_code_hash() { let (ctx, mut ctx_handle) = make_subsystem_context(TaskExecutor::new()); let relay_parent = [1; 32].into(); let para_a = ParaId::from(5_u32); let para_b = ParaId::from(6_u32); let spawner = pezsp_core::testing::TaskExecutor::new(); let validation_code_hash = dummy_validation_code().hash(); let mut subsystem_client = MockSubsystemClient::default(); subsystem_client.validation_code_hash.insert(para_a, validation_code_hash); let subsystem_client = Arc::new(subsystem_client); let subsystem = RuntimeApiSubsystem::new(subsystem_client.clone(), Metrics(None), SpawnGlue(spawner)); let subsystem_task = run(ctx, subsystem).map(|x| x.unwrap()); let test_task = async move { let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::ValidationCodeHash(para_a, OccupiedCoreAssumption::Included, tx), ), }) .await; assert_eq!(rx.await.unwrap().unwrap(), Some(validation_code_hash)); let (tx, rx) = oneshot::channel(); ctx_handle .send(FromOrchestra::Communication { msg: RuntimeApiMessage::Request( relay_parent, Request::ValidationCodeHash(para_b, OccupiedCoreAssumption::Included, tx), ), }) .await; assert_eq!(rx.await.unwrap().unwrap(), None); ctx_handle.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await; }; futures::executor::block_on(future::join(subsystem_task, test_task)); }