diff --git a/Cargo.lock b/Cargo.lock index b2157d421d8a..8f80291e9983 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7028,10 +7028,14 @@ version = "0.9.19" dependencies = [ "assert_matches", "expander 0.0.6", + "petgraph", + "polkadot-overseer-gen", "proc-macro-crate", "proc-macro2", "quote", "syn", + "thiserror", + "tracing-gum", ] [[package]] @@ -7400,6 +7404,7 @@ dependencies = [ "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-test-helpers", + "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-parachain", diff --git a/bridges/.config/lingua.dic b/bridges/.config/lingua.dic index f4b4d55d6e88..223a962006a2 100644 --- a/bridges/.config/lingua.dic +++ b/bridges/.config/lingua.dic @@ -16,6 +16,7 @@ Best/MS BlockId BlockNumber BridgeStorage +clonable CLI/MS Chain1 Chain2 @@ -177,6 +178,7 @@ plancks polkadot/MS pov-block/MS precommit +proc-macro/MS prometheus proxying provisioner/MS diff --git a/core-primitives/src/lib.rs b/core-primitives/src/lib.rs index 4fdfe146c566..bd0256dc7c9e 100644 --- a/core-primitives/src/lib.rs +++ b/core-primitives/src/lib.rs @@ -60,7 +60,7 @@ pub type ChainId = u32; /// A hash of some data used by the relay chain. pub type Hash = sp_core::H256; -/// Unit type wrapper around [`Hash`] that represents a candidate hash. +/// Unit type wrapper around [`type@Hash`] that represents a candidate hash. /// /// This type is produced by [`CandidateReceipt::hash`]. /// diff --git a/node/collation-generation/src/lib.rs b/node/collation-generation/src/lib.rs index aee408637a99..b68a46335cb3 100644 --- a/node/collation-generation/src/lib.rs +++ b/node/collation-generation/src/lib.rs @@ -22,9 +22,9 @@ use futures::{channel::mpsc, future::FutureExt, join, select, sink::SinkExt, str use parity_scale_codec::Encode; use polkadot_node_primitives::{AvailableData, CollationGenerationConfig, PoV}; use polkadot_node_subsystem::{ - messages::{AllMessages, CollationGenerationMessage, CollatorProtocolMessage}, + messages::{CollationGenerationMessage, CollatorProtocolMessage}, overseer, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemContext, - SubsystemError, SubsystemResult, SubsystemSender, + SubsystemError, SubsystemResult, }; use polkadot_node_subsystem_util::{ request_availability_cores, request_persisted_validation_data, request_validation_code, @@ -54,6 +54,7 @@ pub struct CollationGenerationSubsystem { metrics: Metrics, } +#[overseer::contextbounds(CollationGeneration, prefix = self::overseer)] impl CollationGenerationSubsystem { /// Create a new instance of the `CollationGenerationSubsystem`. pub fn new(metrics: Metrics) -> Self { @@ -71,11 +72,7 @@ impl CollationGenerationSubsystem { /// /// If `err_tx` is not `None`, errors are forwarded onto that channel as they occur. /// Otherwise, most are logged and then discarded. - async fn run(mut self, mut ctx: Context) - where - Context: SubsystemContext, - Context: overseer::SubsystemContext, - { + async fn run(mut self, mut ctx: Context) { // when we activate new leaves, we spawn a bunch of sub-tasks, each of which is // expected to generate precisely one message. We don't want to block the main loop // at any point waiting for them all, so instead, we create a channel on which they can @@ -108,12 +105,8 @@ impl CollationGenerationSubsystem { &mut self, incoming: SubsystemResult::Message>>, ctx: &mut Context, - sender: &mpsc::Sender, - ) -> bool - where - Context: SubsystemContext, - Context: overseer::SubsystemContext, - { + sender: &mpsc::Sender, + ) -> bool { match incoming { Ok(FromOverseer::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate { activated, @@ -162,11 +155,8 @@ impl CollationGenerationSubsystem { } } -impl overseer::Subsystem for CollationGenerationSubsystem -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::subsystem(CollationGeneration, error=SubsystemError, prefix=self::overseer)] +impl CollationGenerationSubsystem { fn start(self, ctx: Context) -> SpawnedSubsystem { let future = async move { self.run(ctx).await; @@ -178,12 +168,13 @@ where } } -async fn handle_new_activations( +#[overseer::contextbounds(CollationGeneration, prefix = self::overseer)] +async fn handle_new_activations( config: Arc, activated: impl IntoIterator, ctx: &mut Context, metrics: Metrics, - sender: &mpsc::Sender, + sender: &mpsc::Sender, ) -> crate::error::Result<()> { // follow the procedure from the guide: // https://w3f.github.io/parachain-implementers-guide/node/collators/collation-generation.html @@ -393,9 +384,10 @@ async fn handle_new_activations( metrics.on_collation_generated(); if let Err(err) = task_sender - .send(AllMessages::CollatorProtocol( - CollatorProtocolMessage::DistributeCollation(ccr, pov, result_sender), - )) + .send( + CollatorProtocolMessage::DistributeCollation(ccr, pov, result_sender) + .into(), + ) .await { gum::warn!( @@ -417,7 +409,7 @@ async fn obtain_current_validation_code_hash( relay_parent: Hash, para_id: ParaId, assumption: OccupiedCoreAssumption, - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::CollationGenerationSenderTrait, ) -> Result, crate::error::Error> { use polkadot_node_subsystem::RuntimeApiError; diff --git a/node/collation-generation/src/tests.rs b/node/collation-generation/src/tests.rs index 90e241877147..ca75666c85e3 100644 --- a/node/collation-generation/src/tests.rs +++ b/node/collation-generation/src/tests.rs @@ -296,7 +296,7 @@ mod handle_new_activations { *subsystem_sent_messages.lock().await = rx.collect().await; }); - let sent_messages = Arc::try_unwrap(sent_messages) + let mut sent_messages = Arc::try_unwrap(sent_messages) .expect("subsystem should have shut down by now") .into_inner(); @@ -328,7 +328,7 @@ mod handle_new_activations { }; assert_eq!(sent_messages.len(), 1); - match &sent_messages[0] { + match AllMessages::from(sent_messages.pop().unwrap()) { AllMessages::CollatorProtocol(CollatorProtocolMessage::DistributeCollation( CandidateReceipt { descriptor, .. }, _pov, @@ -356,7 +356,7 @@ mod handle_new_activations { expect_descriptor.erasure_root = descriptor.erasure_root.clone(); expect_descriptor }; - assert_eq!(descriptor, &expect_descriptor); + assert_eq!(descriptor, expect_descriptor); }, _ => panic!("received wrong message type"), } @@ -470,11 +470,13 @@ mod handle_new_activations { assert_eq!(sent_messages.len(), 1); match &sent_messages[0] { - AllMessages::CollatorProtocol(CollatorProtocolMessage::DistributeCollation( - CandidateReceipt { descriptor, .. }, - _pov, - .., - )) => { + overseer::CollationGenerationOutgoingMessages::CollatorProtocolMessage( + CollatorProtocolMessage::DistributeCollation( + CandidateReceipt { descriptor, .. }, + _pov, + .., + ), + ) => { assert_eq!(expect_validation_code_hash, descriptor.validation_code_hash); }, _ => panic!("received wrong message type"), diff --git a/node/core/approval-voting/src/import.rs b/node/core/approval-voting/src/import.rs index 05df3997bfc2..202e44464f0d 100644 --- a/node/core/approval-voting/src/import.rs +++ b/node/core/approval-voting/src/import.rs @@ -38,7 +38,7 @@ use polkadot_node_subsystem::{ ApprovalDistributionMessage, ChainApiMessage, ChainSelectionMessage, RuntimeApiMessage, RuntimeApiRequest, }, - overseer, RuntimeApiError, SubsystemContext, SubsystemError, SubsystemResult, + overseer, RuntimeApiError, SubsystemError, SubsystemResult, }; use polkadot_node_subsystem_util::{ determine_new_blocks, @@ -107,8 +107,9 @@ enum ImportedBlockInfoError { } /// Computes information about the imported block. Returns an error if the info couldn't be extracted. -async fn imported_block_info( - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), +#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] +async fn imported_block_info( + ctx: &mut Context, env: ImportedBlockInfoEnv<'_>, block_hash: Hash, block_header: &Header, @@ -319,10 +320,11 @@ pub struct BlockImportedCandidates { /// * and return information about all candidates imported under each block. /// /// It is the responsibility of the caller to schedule wakeups for each block. -pub(crate) async fn handle_new_head( - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), +#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] +pub(crate) async fn handle_new_head( + ctx: &mut Context, state: &mut State, - db: &mut OverlayedBackend<'_, impl Backend>, + db: &mut OverlayedBackend<'_, B>, head: Hash, finalized_number: &Option, ) -> SubsystemResult> { @@ -609,7 +611,7 @@ pub(crate) mod tests { use assert_matches::assert_matches; use merlin::Transcript; use polkadot_node_primitives::approval::{VRFOutput, VRFProof}; - use polkadot_node_subsystem::messages::AllMessages; + use polkadot_node_subsystem::messages::{AllMessages, ApprovalVotingMessage}; use polkadot_node_subsystem_test_helpers::make_subsystem_context; use polkadot_node_subsystem_util::database::Database; use polkadot_primitives::v2::{Id as ParaId, SessionInfo, ValidatorIndex}; @@ -724,7 +726,8 @@ pub(crate) mod tests { #[test] fn imported_block_info_is_good() { let pool = TaskExecutor::new(); - let (mut ctx, mut handle) = make_subsystem_context::<(), _>(pool.clone()); + let (mut ctx, mut handle) = + make_subsystem_context::(pool.clone()); let session = 5; let session_info = dummy_session_info(session); @@ -847,7 +850,8 @@ pub(crate) mod tests { #[test] fn imported_block_info_fails_if_no_babe_vrf() { let pool = TaskExecutor::new(); - let (mut ctx, mut handle) = make_subsystem_context::<(), _>(pool.clone()); + let (mut ctx, mut handle) = + make_subsystem_context::(pool.clone()); let session = 5; let session_info = dummy_session_info(session); @@ -950,7 +954,8 @@ pub(crate) mod tests { #[test] fn imported_block_info_fails_if_ancient_session() { let pool = TaskExecutor::new(); - let (mut ctx, mut handle) = make_subsystem_context::<(), _>(pool.clone()); + let (mut ctx, mut handle) = + make_subsystem_context::(pool.clone()); let session = 5; @@ -1027,7 +1032,7 @@ pub(crate) mod tests { #[test] fn imported_block_info_extracts_force_approve() { let pool = TaskExecutor::new(); - let (mut ctx, mut handle) = make_subsystem_context::<(), _>(pool.clone()); + let (mut ctx, mut handle) = make_subsystem_context(pool.clone()); let session = 5; let session_info = dummy_session_info(session); @@ -1158,7 +1163,8 @@ pub(crate) mod tests { let mut overlay_db = OverlayedBackend::new(&db); let pool = TaskExecutor::new(); - let (mut ctx, mut handle) = make_subsystem_context::<(), _>(pool.clone()); + let (mut ctx, mut handle) = + make_subsystem_context::(pool.clone()); let session = 5; let irrelevant = 666; diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index 7c3b1a0812cd..c53c04c690a5 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -37,9 +37,8 @@ use polkadot_node_subsystem::{ ChainSelectionMessage, DisputeCoordinatorMessage, HighestApprovedAncestorBlock, RuntimeApiMessage, RuntimeApiRequest, }, - overseer::{self, SubsystemSender as _}, - FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemContext, SubsystemError, - SubsystemResult, SubsystemSender, + overseer, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemError, SubsystemResult, + SubsystemSender, }; use polkadot_node_subsystem_util::{ database::Database, @@ -355,11 +354,8 @@ impl ApprovalVotingSubsystem { } } -impl overseer::Subsystem for ApprovalVotingSubsystem -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::subsystem(ApprovalVoting, error = SubsystemError, prefix = self::overseer)] +impl ApprovalVotingSubsystem { fn start(self, ctx: Context) -> SpawnedSubsystem { let backend = DbBackend::new(self.db.clone(), self.db_config); let future = run::( @@ -597,27 +593,34 @@ struct State { assignment_criteria: Box, } +#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] impl State { fn session_info(&self, i: SessionIndex) -> Option<&SessionInfo> { self.session_window.as_ref().and_then(|w| w.session_info(i)) } /// Bring `session_window` up to date. - pub async fn cache_session_info_for_head( + pub async fn cache_session_info_for_head( &mut self, - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + ctx: &mut Context, head: Hash, - ) -> Result, SessionsUnavailable> { + ) -> Result, SessionsUnavailable> + where + ::Sender: Sized + Send, + { let session_window = self.session_window.take(); match session_window { None => { + let sender = ctx.sender().clone(); self.session_window = - Some(RollingSessionWindow::new(ctx, APPROVAL_SESSIONS, head).await?); + Some(RollingSessionWindow::new(sender, APPROVAL_SESSIONS, head).await?); Ok(None) }, Some(mut session_window) => { - let r = - session_window.cache_session_info_for_head(ctx, head).await.map(Option::Some); + let r = session_window + .cache_session_info_for_head(ctx.sender(), head) + .await + .map(Option::Some); self.session_window = Some(session_window); r }, @@ -701,6 +704,7 @@ enum Action { Conclude, } +#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] async fn run( mut ctx: Context, mut subsystem: ApprovalVotingSubsystem, @@ -709,8 +713,6 @@ async fn run( mut backend: B, ) -> SubsystemResult<()> where - Context: SubsystemContext, - Context: overseer::SubsystemContext, B: Backend, { let mut state = State { @@ -848,9 +850,9 @@ where // https://github.com/paritytech/polkadot/issues/3311 // // returns `true` if any of the actions was a `Conclude` command. -async fn handle_actions( - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), +#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] +async fn handle_actions( + ctx: &mut Context, state: &mut State, overlayed_db: &mut OverlayedBackend<'_, impl Backend>, metrics: &Metrics, @@ -868,7 +870,6 @@ async fn handle_actions( Action::ScheduleWakeup { block_hash, block_number, candidate_hash, tick } => wakeups.schedule(block_hash, block_number, candidate_hash, tick), Action::IssueApproval(candidate_hash, approval_request) => { - let mut sender = ctx.sender().clone(); // Note that the IssueApproval action will create additional // actions that will need to all be processed before we can // handle the next action in the set passed to the ambient @@ -881,7 +882,7 @@ async fn handle_actions( // Note that chaining these iterators is O(n) as we must consume // the prior iterator. let next_actions: Vec = issue_approval( - &mut sender, + ctx, state, overlayed_db, metrics, @@ -1086,9 +1087,9 @@ fn distribution_messages_for_activation( } // Handle an incoming signal from the overseer. Returns true if execution should conclude. -async fn handle_from_overseer( - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), +#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] +async fn handle_from_overseer( + ctx: &mut Context, state: &mut State, db: &mut OverlayedBackend<'_, impl Backend>, metrics: &Metrics, @@ -1197,8 +1198,9 @@ async fn handle_from_overseer( Ok(actions) } -async fn handle_approved_ancestor( - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), +#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] +async fn handle_approved_ancestor( + ctx: &mut Context, db: &OverlayedBackend<'_, impl Backend>, target: Hash, lower_bound: BlockNumber, @@ -2147,9 +2149,9 @@ fn process_wakeup( // Launch approval work, returning an `AbortHandle` which corresponds to the background task // spawned. When the background work is no longer needed, the `AbortHandle` should be dropped // to cancel the background work and any requests it has spawned. -async fn launch_approval( - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), +#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] +async fn launch_approval( + ctx: &mut Context, metrics: Metrics, session_index: SessionIndex, candidate: CandidateReceipt, @@ -2242,15 +2244,12 @@ async fn launch_approval( ); sender - .send_message( - DisputeCoordinatorMessage::IssueLocalStatement( - session_index, - candidate_hash, - candidate.clone(), - false, - ) - .into(), - ) + .send_message(DisputeCoordinatorMessage::IssueLocalStatement( + session_index, + candidate_hash, + candidate.clone(), + false, + )) .await; metrics_guard.take().on_approval_invalid(); }, @@ -2281,17 +2280,14 @@ async fn launch_approval( let (val_tx, val_rx) = oneshot::channel(); sender - .send_message( - CandidateValidationMessage::ValidateFromExhaustive( - available_data.validation_data, - validation_code, - candidate.clone(), - available_data.pov, - APPROVAL_EXECUTION_TIMEOUT, - val_tx, - ) - .into(), - ) + .send_message(CandidateValidationMessage::ValidateFromExhaustive( + available_data.validation_data, + validation_code, + candidate.clone(), + available_data.pov, + APPROVAL_EXECUTION_TIMEOUT, + val_tx, + )) .await; match val_rx.await { @@ -2309,15 +2305,12 @@ async fn launch_approval( } else { // Commitments mismatch - issue a dispute. sender - .send_message( - DisputeCoordinatorMessage::IssueLocalStatement( - session_index, - candidate_hash, - candidate.clone(), - false, - ) - .into(), - ) + .send_message(DisputeCoordinatorMessage::IssueLocalStatement( + session_index, + candidate_hash, + candidate.clone(), + false, + )) .await; metrics_guard.take().on_approval_invalid(); @@ -2334,15 +2327,12 @@ async fn launch_approval( ); sender - .send_message( - DisputeCoordinatorMessage::IssueLocalStatement( - session_index, - candidate_hash, - candidate.clone(), - false, - ) - .into(), - ) + .send_message(DisputeCoordinatorMessage::IssueLocalStatement( + session_index, + candidate_hash, + candidate.clone(), + false, + )) .await; metrics_guard.take().on_approval_invalid(); @@ -2368,8 +2358,9 @@ async fn launch_approval( // Issue and import a local approval vote. Should only be invoked after approval checks // have been done. -async fn issue_approval( - ctx: &mut impl SubsystemSender, +#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] +async fn issue_approval( + ctx: &mut Context, state: &mut State, db: &mut OverlayedBackend<'_, impl Backend>, metrics: &Metrics, @@ -2527,15 +2518,14 @@ async fn issue_approval( metrics.on_approval_produced(); // dispatch to approval distribution. - ctx.send_unbounded_message( - ApprovalDistributionMessage::DistributeApproval(IndirectSignedApprovalVote { + ctx.send_unbounded_message(ApprovalDistributionMessage::DistributeApproval( + IndirectSignedApprovalVote { block_hash, candidate_index: candidate_index as _, validator: validator_index, signature: sig, - }) - .into(), - ); + }, + )); // dispatch to dispute coordinator. actions.extend(inform_disputes_action); diff --git a/node/core/av-store/src/lib.rs b/node/core/av-store/src/lib.rs index dbb30fdda02a..086ce0255cf9 100644 --- a/node/core/av-store/src/lib.rs +++ b/node/core/av-store/src/lib.rs @@ -36,8 +36,7 @@ use polkadot_node_primitives::{AvailableData, ErasureChunk}; use polkadot_node_subsystem::{ errors::{ChainApiError, RuntimeApiError}, messages::{AvailabilityStoreMessage, ChainApiMessage}, - overseer, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemContext, - SubsystemError, + overseer, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemError, }; use polkadot_node_subsystem_util as util; use polkadot_primitives::v2::{ @@ -519,23 +518,17 @@ impl KnownUnfinalizedBlocks { } } -impl overseer::Subsystem for AvailabilityStoreSubsystem -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::subsystem(AvailabilityStore, error=SubsystemError, prefix=self::overseer)] +impl AvailabilityStoreSubsystem { fn start(self, ctx: Context) -> SpawnedSubsystem { - let future = run(self, ctx).map(|_| Ok(())).boxed(); + let future = run::(self, ctx).map(|_| Ok(())).boxed(); SpawnedSubsystem { name: "availability-store-subsystem", future } } } -async fn run(mut subsystem: AvailabilityStoreSubsystem, mut ctx: Context) -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::contextbounds(AvailabilityStore, prefix = self::overseer)] +async fn run(mut subsystem: AvailabilityStoreSubsystem, mut ctx: Context) { let mut next_pruning = Delay::new(subsystem.pruning_config.pruning_interval).fuse(); loop { @@ -556,15 +549,12 @@ where } } +#[overseer::contextbounds(AvailabilityStore, prefix = self::overseer)] async fn run_iteration( ctx: &mut Context, subsystem: &mut AvailabilityStoreSubsystem, mut next_pruning: &mut future::Fuse, -) -> Result -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> Result { select! { incoming = ctx.recv().fuse() => { match incoming.map_err(|_| Error::ContextChannelClosed)? { @@ -608,15 +598,12 @@ where Ok(false) } +#[overseer::contextbounds(AvailabilityStore, prefix = self::overseer)] async fn process_block_activated( ctx: &mut Context, subsystem: &mut AvailabilityStoreSubsystem, activated: Hash, -) -> Result<(), Error> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> Result<(), Error> { let now = subsystem.clock.now()?; let block_header = { @@ -663,6 +650,7 @@ where Ok(()) } +#[overseer::contextbounds(AvailabilityStore, prefix = self::overseer)] async fn process_new_head( ctx: &mut Context, db: &Arc, @@ -672,11 +660,7 @@ async fn process_new_head( now: Duration, hash: Hash, header: Header, -) -> Result<(), Error> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> Result<(), Error> { let candidate_events = util::request_candidate_events(hash, ctx.sender()).await.await??; // We need to request the number of validators based on the parent state, @@ -814,16 +798,13 @@ macro_rules! peek_num { }; } +#[overseer::contextbounds(AvailabilityStore, prefix = self::overseer)] async fn process_block_finalized( ctx: &mut Context, subsystem: &AvailabilityStoreSubsystem, finalized_hash: Hash, finalized_number: BlockNumber, -) -> Result<(), Error> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> Result<(), Error> { let now = subsystem.clock.now()?; let mut next_possible_batch = 0; diff --git a/node/core/av-store/src/tests.rs b/node/core/av-store/src/tests.rs index 8c8a65864f8d..9ef9666d619a 100644 --- a/node/core/av-store/src/tests.rs +++ b/node/core/av-store/src/tests.rs @@ -309,13 +309,13 @@ fn store_chunk_works() { let chunk_msg = AvailabilityStoreMessage::StoreChunk { candidate_hash, chunk: chunk.clone(), tx }; - overseer_send(&mut virtual_overseer, chunk_msg.into()).await; + overseer_send(&mut virtual_overseer, chunk_msg).await; assert_eq!(rx.await.unwrap(), Ok(())); let (tx, rx) = oneshot::channel(); let query_chunk = AvailabilityStoreMessage::QueryChunk(candidate_hash, validator_index, tx); - overseer_send(&mut virtual_overseer, query_chunk.into()).await; + overseer_send(&mut virtual_overseer, query_chunk).await; assert_eq!(rx.await.unwrap().unwrap(), chunk); virtual_overseer @@ -341,13 +341,13 @@ fn store_chunk_does_nothing_if_no_entry_already() { let chunk_msg = AvailabilityStoreMessage::StoreChunk { candidate_hash, chunk: chunk.clone(), tx }; - overseer_send(&mut virtual_overseer, chunk_msg.into()).await; + overseer_send(&mut virtual_overseer, chunk_msg).await; assert_eq!(rx.await.unwrap(), Err(())); let (tx, rx) = oneshot::channel(); let query_chunk = AvailabilityStoreMessage::QueryChunk(candidate_hash, validator_index, tx); - overseer_send(&mut virtual_overseer, query_chunk.into()).await; + overseer_send(&mut virtual_overseer, query_chunk).await; assert!(rx.await.unwrap().is_none()); virtual_overseer diff --git a/node/core/backing/src/lib.rs b/node/core/backing/src/lib.rs index 264e7e27016d..c6c1a76cee61 100644 --- a/node/core/backing/src/lib.rs +++ b/node/core/backing/src/lib.rs @@ -37,13 +37,12 @@ use polkadot_node_primitives::{ use polkadot_node_subsystem::{ jaeger, messages::{ - AllMessages, AvailabilityDistributionMessage, AvailabilityStoreMessage, - CandidateBackingMessage, CandidateValidationMessage, CollatorProtocolMessage, - DisputeCoordinatorMessage, ProvisionableData, ProvisionerMessage, RuntimeApiRequest, - StatementDistributionMessage, + AvailabilityDistributionMessage, AvailabilityStoreMessage, CandidateBackingMessage, + CandidateValidationMessage, CollatorProtocolMessage, DisputeCoordinatorMessage, + ProvisionableData, ProvisionerMessage, RuntimeApiRequest, StatementDistributionMessage, }, overseer, ActiveLeavesUpdate, FromOverseer, OverseerSignal, PerLeafSpan, SpawnedSubsystem, - Stage, SubsystemContext, SubsystemError, SubsystemSender, + Stage, SubsystemError, }; use polkadot_node_subsystem_util::{ self as util, request_from_runtime, request_session_index_for_child, request_validator_groups, @@ -131,10 +130,10 @@ impl CandidateBackingSubsystem { } } -impl overseer::Subsystem for CandidateBackingSubsystem +#[overseer::subsystem(CandidateBacking, error = SubsystemError, prefix = self::overseer)] +impl CandidateBackingSubsystem where - Context: SubsystemContext, - Context: overseer::SubsystemContext, + Context: Send + Sync, { fn start(self, ctx: Context) -> SpawnedSubsystem { let future = async move { @@ -148,15 +147,12 @@ where } } +#[overseer::contextbounds(CandidateBacking, prefix = self::overseer)] async fn run( mut ctx: Context, keystore: SyncCryptoStorePtr, metrics: Metrics, -) -> FatalResult<()> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> FatalResult<()> { let (background_validation_tx, mut background_validation_rx) = mpsc::channel(16); let mut jobs = HashMap::new(); @@ -180,6 +176,7 @@ where Ok(()) } +#[overseer::contextbounds(CandidateBacking, prefix = self::overseer)] async fn run_iteration( ctx: &mut Context, keystore: SyncCryptoStorePtr, @@ -187,11 +184,7 @@ async fn run_iteration( jobs: &mut HashMap>, background_validation_tx: mpsc::Sender<(Hash, ValidatedCandidateCommand)>, background_validation_rx: &mut mpsc::Receiver<(Hash, ValidatedCandidateCommand)>, -) -> Result<(), Error> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> Result<(), Error> { loop { futures::select!( validated_command = background_validation_rx.next().fuse() => { @@ -225,16 +218,13 @@ where } } +#[overseer::contextbounds(CandidateBacking, prefix = self::overseer)] async fn handle_validated_candidate_command( ctx: &mut Context, jobs: &mut HashMap>, relay_parent: Hash, command: ValidatedCandidateCommand, -) -> Result<(), Error> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> Result<(), Error> { if let Some(job) = jobs.get_mut(&relay_parent) { job.job.handle_validated_candidate_command(&job.span, ctx, command).await?; } else { @@ -245,15 +235,12 @@ where Ok(()) } +#[overseer::contextbounds(CandidateBacking, prefix = self::overseer)] async fn handle_communication( ctx: &mut Context, jobs: &mut HashMap>, message: CandidateBackingMessage, -) -> Result<(), Error> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> Result<(), Error> { match message { CandidateBackingMessage::Second(relay_parent, candidate, pov) => { if let Some(job) = jobs.get_mut(&relay_parent) { @@ -274,6 +261,7 @@ where Ok(()) } +#[overseer::contextbounds(CandidateBacking, prefix = self::overseer)] async fn handle_active_leaves_update( ctx: &mut Context, update: ActiveLeavesUpdate, @@ -281,11 +269,7 @@ async fn handle_active_leaves_update( keystore: &SyncCryptoStorePtr, background_validation_tx: &mpsc::Sender<(Hash, ValidatedCandidateCommand)>, metrics: &Metrics, -) -> Result<(), Error> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> Result<(), Error> { for deactivated in update.deactivated { jobs.remove(&deactivated); } @@ -578,22 +562,19 @@ fn table_attested_to_backed( } async fn store_available_data( - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::CandidateBackingSenderTrait, n_validators: u32, candidate_hash: CandidateHash, available_data: AvailableData, ) -> Result<(), Error> { let (tx, rx) = oneshot::channel(); sender - .send_message( - AvailabilityStoreMessage::StoreAvailableData { - candidate_hash, - n_validators, - available_data, - tx, - } - .into(), - ) + .send_message(AvailabilityStoreMessage::StoreAvailableData { + candidate_hash, + n_validators, + available_data, + tx, + }) .await; let _ = rx.await.map_err(Error::StoreAvailableData)?; @@ -605,8 +586,9 @@ async fn store_available_data( // // This will compute the erasure root internally and compare it to the expected erasure root. // This returns `Err()` iff there is an internal error. Otherwise, it returns either `Ok(Ok(()))` or `Ok(Err(_))`. + async fn make_pov_available( - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::CandidateBackingSenderTrait, n_validators: usize, pov: Arc, candidate_hash: CandidateHash, @@ -639,7 +621,7 @@ async fn make_pov_available( } async fn request_pov( - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::CandidateBackingSenderTrait, relay_parent: Hash, from_validator: ValidatorIndex, candidate_hash: CandidateHash, @@ -647,16 +629,13 @@ async fn request_pov( ) -> Result, Error> { let (tx, rx) = oneshot::channel(); sender - .send_message( - AvailabilityDistributionMessage::FetchPoV { - relay_parent, - from_validator, - candidate_hash, - pov_hash, - tx, - } - .into(), - ) + .send_message(AvailabilityDistributionMessage::FetchPoV { + relay_parent, + from_validator, + candidate_hash, + pov_hash, + tx, + }) .await; let pov = rx.await.map_err(|_| Error::FetchPoV)?; @@ -664,22 +643,19 @@ async fn request_pov( } async fn request_candidate_validation( - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::CandidateBackingSenderTrait, candidate_receipt: CandidateReceipt, pov: Arc, ) -> Result { let (tx, rx) = oneshot::channel(); sender - .send_message( - CandidateValidationMessage::ValidateFromChainState( - candidate_receipt, - pov, - BACKING_EXECUTION_TIMEOUT, - tx, - ) - .into(), - ) + .send_message(CandidateValidationMessage::ValidateFromChainState( + candidate_receipt, + pov, + BACKING_EXECUTION_TIMEOUT, + tx, + )) .await; match rx.await { @@ -692,7 +668,7 @@ async fn request_candidate_validation( type BackgroundValidationResult = Result<(CandidateReceipt, CandidateCommitments, Arc), CandidateReceipt>; -struct BackgroundValidationParams, F> { +struct BackgroundValidationParams { sender: S, tx_command: mpsc::Sender<(Hash, ValidatedCandidateCommand)>, candidate: CandidateReceipt, @@ -705,7 +681,7 @@ struct BackgroundValidationParams, F> async fn validate_and_make_available( params: BackgroundValidationParams< - impl SubsystemSender, + impl overseer::CandidateBackingSenderTrait, impl Fn(BackgroundValidationResult) -> ValidatedCandidateCommand + Sync, >, ) -> Result<(), Error> { @@ -809,11 +785,8 @@ async fn validate_and_make_available( struct ValidatorIndexOutOfBounds; -impl CandidateBackingJob -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::contextbounds(CandidateBacking, prefix = self::overseer)] +impl CandidateBackingJob { async fn handle_validated_candidate_command( &mut self, root_span: &jaeger::Span, @@ -896,7 +869,7 @@ where &mut self, ctx: &mut Context, params: BackgroundValidationParams< - impl SubsystemSender, + impl overseer::CandidateBackingSenderTrait, impl Fn(BackgroundValidationResult) -> ValidatedCandidateCommand + Send + 'static + Sync, >, ) -> Result<(), Error> { @@ -1001,7 +974,7 @@ where } /// Check if there have happened any new misbehaviors and issue necessary messages. - fn issue_new_misbehaviors(&mut self, ctx: &mut Context) { + fn issue_new_misbehaviors(&mut self, sender: &mut impl overseer::CandidateBackingSenderTrait) { // collect the misbehaviors to avoid double mutable self borrow issues let misbehaviors: Vec<_> = self.table.drain_misbehaviors().collect(); for (validator_id, report) in misbehaviors { @@ -1010,7 +983,7 @@ where // // Misbehaviors are bounded by the number of validators and // the block production protocol. - ctx.send_unbounded_message(ProvisionerMessage::ProvisionableData( + sender.send_unbounded_message(ProvisionerMessage::ProvisionableData( self.parent, ProvisionableData::MisbehaviorReport(self.parent, validator_id, report), )); @@ -1042,7 +1015,7 @@ where }; if let Err(ValidatorIndexOutOfBounds) = self - .dispatch_new_statement_to_dispute_coordinator(ctx, candidate_hash, &statement) + .dispatch_new_statement_to_dispute_coordinator(ctx.sender(), candidate_hash, &statement) .await { gum::warn!( @@ -1101,7 +1074,7 @@ where None }; - self.issue_new_misbehaviors(ctx); + self.issue_new_misbehaviors(ctx.sender()); // It is important that the child span is dropped before its parent span (`unbacked_span`) drop(import_statement_span); @@ -1123,8 +1096,8 @@ where /// the networking component responsible for feeding statements to the backing subsystem /// is meant to check the signature and provenance of all statements before submission. async fn dispatch_new_statement_to_dispute_coordinator( - &mut self, - ctx: &mut Context, + &self, + sender: &mut impl overseer::CandidateBackingSenderTrait, candidate_hash: CandidateHash, statement: &SignedFullStatement, ) -> Result<(), ValidatorIndexOutOfBounds> { @@ -1157,14 +1130,15 @@ where if let (Some(candidate_receipt), Some(dispute_statement)) = (maybe_candidate_receipt, maybe_signed_dispute_statement) { - ctx.send_message(DisputeCoordinatorMessage::ImportStatements { - candidate_hash, - candidate_receipt, - session: self.session_index, - statements: vec![(dispute_statement, validator_index)], - pending_confirmation: None, - }) - .await; + sender + .send_message(DisputeCoordinatorMessage::ImportStatements { + candidate_hash, + candidate_receipt, + session: self.session_index, + statements: vec![(dispute_statement, validator_index)], + pending_confirmation: None, + }) + .await; } Ok(()) diff --git a/node/core/backing/src/tests.rs b/node/core/backing/src/tests.rs index e60d5112be1c..e7fbdf6a4e13 100644 --- a/node/core/backing/src/tests.rs +++ b/node/core/backing/src/tests.rs @@ -23,7 +23,10 @@ use assert_matches::assert_matches; use futures::{future, Future}; use polkadot_node_primitives::{BlockData, InvalidCandidate}; use polkadot_node_subsystem::{ - messages::{CollatorProtocolMessage, RuntimeApiMessage, RuntimeApiRequest, ValidationFailed}, + messages::{ + AllMessages, CollatorProtocolMessage, RuntimeApiMessage, RuntimeApiRequest, + ValidationFailed, + }, ActivatedLeaf, ActiveLeavesUpdate, FromOverseer, LeafStatus, OverseerSignal, }; use polkadot_node_subsystem_test_helpers as test_helpers; diff --git a/node/core/bitfield-signing/src/lib.rs b/node/core/bitfield-signing/src/lib.rs index 94b525ec6274..3b082de92109 100644 --- a/node/core/bitfield-signing/src/lib.rs +++ b/node/core/bitfield-signing/src/lib.rs @@ -34,7 +34,7 @@ use polkadot_node_subsystem::{ AvailabilityStoreMessage, BitfieldDistributionMessage, BitfieldSigningMessage, RuntimeApiMessage, RuntimeApiRequest, }, - ActivatedLeaf, LeafStatus, PerLeafSpan, SubsystemSender, + overseer, ActivatedLeaf, LeafStatus, PerLeafSpan, SubsystemSender, }; use polkadot_node_subsystem_util::{self as util, JobSender, JobSubsystem, JobTrait, Validator}; use polkadot_primitives::v2::{AvailabilityBitfield, CoreState, Hash, ValidatorIndex}; @@ -53,7 +53,7 @@ const JOB_DELAY: Duration = Duration::from_millis(1500); const LOG_TARGET: &str = "parachain::bitfield-signing"; /// Each `BitfieldSigningJob` prepares a signed bitfield for a single relay parent. -pub struct BitfieldSigningJob; +pub struct BitfieldSigningJob(std::marker::PhantomData); /// Errors we may encounter in the course of executing the `BitfieldSigningSubsystem`. #[derive(Debug, thiserror::Error)] @@ -83,7 +83,7 @@ pub enum Error { async fn get_core_availability( core: &CoreState, validator_idx: ValidatorIndex, - sender: &Mutex<&mut impl SubsystemSender>, + sender: &Mutex<&mut impl SubsystemSender>, span: &jaeger::Span, ) -> Result { if let &CoreState::Occupied(ref core) = core { @@ -122,7 +122,7 @@ async fn get_core_availability( /// delegates to the v1 runtime API async fn get_availability_cores( relay_parent: Hash, - sender: &mut impl SubsystemSender, + sender: &mut impl SubsystemSender, ) -> Result, Error> { let (tx, rx) = oneshot::channel(); sender @@ -146,7 +146,7 @@ async fn construct_availability_bitfield( relay_parent: Hash, span: &jaeger::Span, validator_idx: ValidatorIndex, - sender: &mut impl SubsystemSender, + sender: &mut impl SubsystemSender, ) -> Result { // get the set of availability cores from the runtime let availability_cores = { @@ -182,8 +182,13 @@ async fn construct_availability_bitfield( Ok(AvailabilityBitfield(core_bits)) } -impl JobTrait for BitfieldSigningJob { +impl JobTrait for BitfieldSigningJob +where + Sender: overseer::BitfieldSigningSenderTrait + Unpin, +{ type ToJob = BitfieldSigningMessage; + type OutgoingMessages = overseer::BitfieldSigningOutgoingMessages; + type Sender = Sender; type Error = Error; type RunArgs = SyncCryptoStorePtr; type Metrics = Metrics; @@ -191,12 +196,12 @@ impl JobTrait for BitfieldSigningJob { const NAME: &'static str = "bitfield-signing-job"; /// Run a job for the parent block indicated - fn run( + fn run( leaf: ActivatedLeaf, keystore: Self::RunArgs, metrics: Self::Metrics, _receiver: mpsc::Receiver, - mut sender: JobSender, + mut sender: JobSender, ) -> Pin> + Send>> { let metrics = metrics.clone(); async move { @@ -286,4 +291,5 @@ impl JobTrait for BitfieldSigningJob { } /// `BitfieldSigningSubsystem` manages a number of bitfield signing jobs. -pub type BitfieldSigningSubsystem = JobSubsystem; +pub type BitfieldSigningSubsystem = + JobSubsystem, Spawner>; diff --git a/node/core/candidate-validation/src/lib.rs b/node/core/candidate-validation/src/lib.rs index 6da1bc0ac96c..2fdcc976219b 100644 --- a/node/core/candidate-validation/src/lib.rs +++ b/node/core/candidate-validation/src/lib.rs @@ -35,8 +35,8 @@ use polkadot_node_subsystem::{ CandidateValidationMessage, PreCheckOutcome, RuntimeApiMessage, RuntimeApiRequest, ValidationFailed, }, - overseer, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemContext, SubsystemError, - SubsystemResult, SubsystemSender, + overseer, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemError, SubsystemResult, + SubsystemSender, }; use polkadot_parachain::primitives::{ValidationParams, ValidationResult as WasmValidationResult}; use polkadot_primitives::v2::{ @@ -93,11 +93,8 @@ impl CandidateValidationSubsystem { } } -impl overseer::Subsystem for CandidateValidationSubsystem -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::subsystem(CandidateValidation, error=SubsystemError, prefix=self::overseer)] +impl CandidateValidationSubsystem { fn start(self, ctx: Context) -> SpawnedSubsystem { let future = run( ctx, @@ -112,17 +109,14 @@ where } } +#[overseer::contextbounds(CandidateValidation, prefix = self::overseer)] async fn run( mut ctx: Context, metrics: Metrics, pvf_metrics: polkadot_node_core_pvf::Metrics, cache_path: PathBuf, program_path: PathBuf, -) -> SubsystemResult<()> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> SubsystemResult<()> { let (validation_host, task) = polkadot_node_core_pvf::start( polkadot_node_core_pvf::Config::new(cache_path, program_path), pvf_metrics, @@ -235,7 +229,7 @@ async fn runtime_api_request( receiver: oneshot::Receiver>, ) -> Result where - Sender: SubsystemSender, + Sender: SubsystemSender, { sender .send_message(RuntimeApiMessage::Request(relay_parent, request).into()) @@ -268,7 +262,7 @@ async fn request_validation_code_by_hash( validation_code_hash: ValidationCodeHash, ) -> Result, RuntimeRequestFailed> where - Sender: SubsystemSender, + Sender: SubsystemSender, { let (tx, rx) = oneshot::channel(); runtime_api_request( @@ -287,7 +281,7 @@ async fn precheck_pvf( validation_code_hash: ValidationCodeHash, ) -> PreCheckOutcome where - Sender: SubsystemSender, + Sender: SubsystemSender, { let validation_code = match request_validation_code_by_hash(sender, relay_parent, validation_code_hash).await { @@ -342,7 +336,7 @@ async fn check_assumption_validation_data( assumption: OccupiedCoreAssumption, ) -> AssumptionCheckOutcome where - Sender: SubsystemSender, + Sender: SubsystemSender, { let validation_data = { let (tx, rx) = oneshot::channel(); @@ -386,7 +380,7 @@ async fn find_assumed_validation_data( descriptor: &CandidateDescriptor, ) -> AssumptionCheckOutcome where - Sender: SubsystemSender, + Sender: SubsystemSender, { // The candidate descriptor has a `persisted_validation_data_hash` which corresponds to // one of up to two possible values that we can derive from the state of the @@ -421,7 +415,7 @@ pub async fn find_validation_data( descriptor: &CandidateDescriptor, ) -> Result, ValidationFailed> where - Sender: SubsystemSender, + Sender: SubsystemSender, { match find_assumed_validation_data(sender, &descriptor).await { AssumptionCheckOutcome::Matches(validation_data, validation_code) => @@ -446,7 +440,7 @@ async fn validate_from_chain_state( metrics: &Metrics, ) -> Result where - Sender: SubsystemSender, + Sender: SubsystemSender, { let mut new_sender = sender.clone(); let (validation_data, validation_code) = diff --git a/node/core/chain-api/src/lib.rs b/node/core/chain-api/src/lib.rs index 36282a2edccc..3543183654f0 100644 --- a/node/core/chain-api/src/lib.rs +++ b/node/core/chain-api/src/lib.rs @@ -39,7 +39,7 @@ use sp_blockchain::HeaderBackend; use polkadot_node_subsystem::{ messages::ChainApiMessage, overseer, FromOverseer, OverseerSignal, SpawnedSubsystem, - SubsystemContext, SubsystemError, SubsystemResult, + SubsystemError, SubsystemResult, }; use polkadot_primitives::v2::{Block, BlockId}; @@ -64,11 +64,10 @@ impl ChainApiSubsystem { } } -impl overseer::Subsystem for ChainApiSubsystem +#[overseer::subsystem(ChainApi, error = SubsystemError, prefix = self::overseer)] +impl ChainApiSubsystem where Client: HeaderBackend + AuxStore + 'static, - Context: SubsystemContext, - Context: overseer::SubsystemContext, { fn start(self, ctx: Context) -> SpawnedSubsystem { let future = run::(ctx, self) @@ -78,14 +77,13 @@ where } } +#[overseer::contextbounds(ChainApi, prefix = self::overseer)] async fn run( mut ctx: Context, subsystem: ChainApiSubsystem, ) -> SubsystemResult<()> where Client: HeaderBackend + AuxStore, - Context: SubsystemContext, - Context: overseer::SubsystemContext, { loop { match ctx.recv().await? { diff --git a/node/core/chain-selection/src/lib.rs b/node/core/chain-selection/src/lib.rs index 0728ed23c043..464edf69f13b 100644 --- a/node/core/chain-selection/src/lib.rs +++ b/node/core/chain-selection/src/lib.rs @@ -20,7 +20,8 @@ use polkadot_node_primitives::BlockWeight; use polkadot_node_subsystem::{ errors::ChainApiError, messages::{ChainApiMessage, ChainSelectionMessage}, - overseer, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemContext, SubsystemError, + overseer::{self, SubsystemSender}, + FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemError, }; use polkadot_node_subsystem_util::database::Database; use polkadot_primitives::v2::{BlockNumber, ConsensusLog, Hash, Header}; @@ -328,11 +329,8 @@ impl ChainSelectionSubsystem { } } -impl overseer::Subsystem for ChainSelectionSubsystem -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::subsystem(ChainSelection, error = SubsystemError, prefix = self::overseer)] +impl ChainSelectionSubsystem { fn start(self, ctx: Context) -> SpawnedSubsystem { let backend = db_backend::v1::DbBackend::new( self.db, @@ -348,14 +346,13 @@ where } } +#[overseer::contextbounds(ChainSelection, prefix = self::overseer)] async fn run( mut ctx: Context, mut backend: B, stagnant_check_interval: StagnantCheckInterval, clock: Box, ) where - Context: SubsystemContext, - Context: overseer::SubsystemContext, B: Backend, { loop { @@ -363,7 +360,7 @@ async fn run( match res { Err(e) => { e.trace(); - // All errors right now are considered fatal: + // All errors are considered fatal right now: break }, Ok(()) => { @@ -379,6 +376,7 @@ async fn run( // // A return value of `Ok` indicates that an exit should be made, while non-fatal errors // lead to another call to this function. +#[overseer::contextbounds(ChainSelection, prefix = self::overseer)] async fn run_until_error( ctx: &mut Context, backend: &mut B, @@ -386,8 +384,6 @@ async fn run_until_error( clock: &(dyn Clock + Sync), ) -> Result<(), Error> where - Context: SubsystemContext, - Context: overseer::SubsystemContext, B: Backend, { let mut stagnant_check_stream = stagnant_check_interval.timeout_stream(); @@ -402,7 +398,7 @@ where FromOverseer::Signal(OverseerSignal::ActiveLeaves(update)) => { for leaf in update.activated { let write_ops = handle_active_leaf( - ctx, + ctx.sender(), &*backend, clock.timestamp_now() + STAGNANT_TIMEOUT, leaf.hash, @@ -419,7 +415,7 @@ where handle_approved_block(backend, hash)? } ChainSelectionMessage::Leaves(tx) => { - let leaves = load_leaves(ctx, &*backend).await?; + let leaves = load_leaves(ctx.sender(), &*backend).await?; let _ = tx.send(leaves); } ChainSelectionMessage::BestLeafContaining(required, tx) => { @@ -446,11 +442,11 @@ where } async fn fetch_finalized( - ctx: &mut impl SubsystemContext, + sender: &mut impl SubsystemSender, ) -> Result, Error> { let (number_tx, number_rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::FinalizedBlockNumber(number_tx)).await; + sender.send_message(ChainApiMessage::FinalizedBlockNumber(number_tx)).await; let number = match number_rx.await? { Ok(number) => number, @@ -462,7 +458,7 @@ async fn fetch_finalized( let (hash_tx, hash_rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::FinalizedBlockHash(number, hash_tx)).await; + sender.send_message(ChainApiMessage::FinalizedBlockHash(number, hash_tx)).await; match hash_rx.await? { Err(err) => { @@ -478,11 +474,11 @@ async fn fetch_finalized( } async fn fetch_header( - ctx: &mut impl SubsystemContext, + sender: &mut impl SubsystemSender, hash: Hash, ) -> Result, Error> { let (tx, rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::BlockHeader(hash, tx)).await; + sender.send_message(ChainApiMessage::BlockHeader(hash, tx)).await; Ok(rx.await?.unwrap_or_else(|err| { gum::warn!(target: LOG_TARGET, ?hash, ?err, "Missing hash for finalized block number"); @@ -491,11 +487,11 @@ async fn fetch_header( } async fn fetch_block_weight( - ctx: &mut impl SubsystemContext, + sender: &mut impl overseer::SubsystemSender, hash: Hash, ) -> Result, Error> { let (tx, rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::BlockWeight(hash, tx)).await; + sender.send_message(ChainApiMessage::BlockWeight(hash, tx)).await; let res = rx.await?; @@ -507,7 +503,7 @@ async fn fetch_block_weight( // Handle a new active leaf. async fn handle_active_leaf( - ctx: &mut impl SubsystemContext, + sender: &mut impl overseer::ChainSelectionSenderTrait, backend: &impl Backend, stagnant_at: Timestamp, hash: Hash, @@ -519,10 +515,10 @@ async fn handle_active_leaf( // tree. l.saturating_sub(1) }, - None => fetch_finalized(ctx).await?.map_or(1, |(_, n)| n), + None => fetch_finalized(sender).await?.map_or(1, |(_, n)| n), }; - let header = match fetch_header(ctx, hash).await? { + let header = match fetch_header(sender, hash).await? { None => { gum::warn!(target: LOG_TARGET, ?hash, "Missing header for new head"); return Ok(Vec::new()) @@ -531,7 +527,7 @@ async fn handle_active_leaf( }; let new_blocks = polkadot_node_subsystem_util::determine_new_blocks( - ctx.sender(), + sender, |h| backend.load_block_entry(h).map(|b| b.is_some()), hash, &header, @@ -544,7 +540,7 @@ async fn handle_active_leaf( // determine_new_blocks gives blocks in descending order. // for this, we want ascending order. for (hash, header) in new_blocks.into_iter().rev() { - let weight = match fetch_block_weight(ctx, hash).await? { + let weight = match fetch_block_weight(sender, hash).await? { None => { gum::warn!( target: LOG_TARGET, @@ -654,13 +650,13 @@ fn detect_stagnant(backend: &mut impl Backend, now: Timestamp) -> Result<(), Err // Load the leaves from the backend. If there are no leaves, then return // the finalized block. async fn load_leaves( - ctx: &mut impl SubsystemContext, + sender: &mut impl overseer::SubsystemSender, backend: &impl Backend, ) -> Result, Error> { let leaves: Vec<_> = backend.load_leaves()?.into_hashes_descending().collect(); if leaves.is_empty() { - Ok(fetch_finalized(ctx).await?.map_or(Vec::new(), |(h, _)| vec![h])) + Ok(fetch_finalized(sender).await?.map_or(Vec::new(), |(h, _)| vec![h])) } else { Ok(leaves) } diff --git a/node/core/dispute-coordinator/src/initialized.rs b/node/core/dispute-coordinator/src/initialized.rs index 40f9ed8211ff..00817a136334 100644 --- a/node/core/dispute-coordinator/src/initialized.rs +++ b/node/core/dispute-coordinator/src/initialized.rs @@ -34,7 +34,7 @@ use polkadot_node_subsystem::{ BlockDescription, DisputeCoordinatorMessage, DisputeDistributionMessage, ImportStatementsResult, }, - overseer, ActivatedLeaf, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SubsystemContext, + overseer, ActivatedLeaf, ActiveLeavesUpdate, FromOverseer, OverseerSignal, }; use polkadot_node_subsystem_util::rolling_session_window::{ RollingSessionWindow, SessionWindowUpdate, SessionsUnavailable, @@ -83,6 +83,7 @@ pub struct Initialized { error: Option, } +#[overseer::contextbounds(DisputeCoordinator, prefix = self::overseer)] impl Initialized { /// Make initialized subsystem, ready to `run`. pub fn new( @@ -123,8 +124,6 @@ impl Initialized { clock: Box, ) -> FatalResult<()> where - Context: overseer::SubsystemContext, - Context: SubsystemContext, B: Backend, { loop { @@ -161,8 +160,6 @@ impl Initialized { clock: &dyn Clock, ) -> Result<()> where - Context: overseer::SubsystemContext, - Context: SubsystemContext, B: Backend, { for (priority, request) in participations.drain(..) { @@ -253,10 +250,9 @@ impl Initialized { } } - async fn process_active_leaves_update( + async fn process_active_leaves_update( &mut self, - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + ctx: &mut Context, overlay_db: &mut OverlayedBackend<'_, impl Backend>, update: ActiveLeavesUpdate, now: u64, @@ -268,7 +264,7 @@ impl Initialized { if let Some(new_leaf) = update.activated { match self .rolling_session_window - .cache_session_info_for_head(ctx, new_leaf.hash) + .cache_session_info_for_head(ctx.sender(), new_leaf.hash) .await { Err(e) => { @@ -318,10 +314,9 @@ impl Initialized { /// Scrapes on-chain votes (backing votes and concluded disputes) for a active leaf of the /// relay chain. - async fn process_on_chain_votes( + async fn process_on_chain_votes( &mut self, - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + ctx: &mut Context, overlay_db: &mut OverlayedBackend<'_, impl Backend>, votes: ScrapedOnChainVotes, now: u64, @@ -497,9 +492,9 @@ impl Initialized { Ok(()) } - async fn handle_incoming( + async fn handle_incoming( &mut self, - ctx: &mut impl SubsystemContext, + ctx: &mut Context, overlay_db: &mut OverlayedBackend<'_, impl Backend>, message: DisputeCoordinatorMessage, now: Timestamp, @@ -634,9 +629,9 @@ impl Initialized { Ok(()) } - async fn handle_import_statements( + async fn handle_import_statements( &mut self, - ctx: &mut impl SubsystemContext, + ctx: &mut Context, overlay_db: &mut OverlayedBackend<'_, impl Backend>, candidate_hash: CandidateHash, candidate_receipt: MaybeCandidateReceipt, @@ -923,9 +918,9 @@ impl Initialized { Ok(ImportStatementsResult::ValidImport) } - async fn issue_local_statement( + async fn issue_local_statement( &mut self, - ctx: &mut impl SubsystemContext, + ctx: &mut Context, overlay_db: &mut OverlayedBackend<'_, impl Backend>, candidate_hash: CandidateHash, candidate_receipt: CandidateReceipt, @@ -1054,10 +1049,10 @@ enum MuxedMessage { Participation(participation::WorkerMessage), } +#[overseer::contextbounds(DisputeCoordinator, prefix = self::overseer)] impl MuxedMessage { - async fn receive( - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + async fn receive( + ctx: &mut Context, from_sender: &mut participation::WorkerMessageReceiver, ) -> FatalResult { // We are only fusing here to make `select` happy, in reality we will quit if the stream diff --git a/node/core/dispute-coordinator/src/lib.rs b/node/core/dispute-coordinator/src/lib.rs index 16b89c06ae41..1a7f1540b8b2 100644 --- a/node/core/dispute-coordinator/src/lib.rs +++ b/node/core/dispute-coordinator/src/lib.rs @@ -32,8 +32,7 @@ use sc_keystore::LocalKeystore; use polkadot_node_primitives::{CandidateVotes, DISPUTE_WINDOW}; use polkadot_node_subsystem::{ - messages::DisputeCoordinatorMessage, overseer, ActivatedLeaf, FromOverseer, OverseerSignal, - SpawnedSubsystem, SubsystemContext, SubsystemError, + overseer, ActivatedLeaf, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemError, }; use polkadot_node_subsystem_util::{ database::Database, rolling_session_window::RollingSessionWindow, @@ -124,11 +123,8 @@ impl Config { } } -impl overseer::Subsystem for DisputeCoordinatorSubsystem -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::subsystem(DisputeCoordinator, error=SubsystemError, prefix=self::overseer)] +impl DisputeCoordinatorSubsystem { fn start(self, ctx: Context) -> SpawnedSubsystem { let future = async { let backend = DbBackend::new(self.store.clone(), self.config.column_config()); @@ -142,6 +138,7 @@ where } } +#[overseer::contextbounds(DisputeCoordinator, prefix = self::overseer)] impl DisputeCoordinatorSubsystem { /// Create a new instance of the subsystem. pub fn new( @@ -161,8 +158,6 @@ impl DisputeCoordinatorSubsystem { clock: Box, ) -> FatalResult<()> where - Context: overseer::SubsystemContext, - Context: SubsystemContext, B: Backend + 'static, { let res = self.initialize(&mut ctx, backend, &*clock).await?; @@ -194,8 +189,6 @@ impl DisputeCoordinatorSubsystem { )>, > where - Context: overseer::SubsystemContext, - Context: SubsystemContext, B: Backend + 'static, { loop { @@ -260,11 +253,7 @@ impl DisputeCoordinatorSubsystem { Vec, SpamSlots, ChainScraper, - )> - where - Context: overseer::SubsystemContext, - Context: SubsystemContext, - { + )> { // Prune obsolete disputes: db::v1::note_current_session(overlay_db, rolling_session_window.latest_session())?; @@ -358,17 +347,15 @@ impl DisputeCoordinatorSubsystem { } /// Wait for `ActiveLeavesUpdate` on startup, returns `None` if `Conclude` signal came first. +#[overseer::contextbounds(DisputeCoordinator, prefix = self::overseer)] async fn get_rolling_session_window( ctx: &mut Context, -) -> Result> -where - Context: overseer::SubsystemContext, - Context: SubsystemContext, -{ - if let Some(leaf) = wait_for_first_leaf(ctx).await? { +) -> Result> { + if let Some(leaf) = { wait_for_first_leaf(ctx) }.await? { + let sender = ctx.sender().clone(); Ok(Some(( leaf.clone(), - RollingSessionWindow::new(ctx, DISPUTE_WINDOW, leaf.hash) + RollingSessionWindow::new(sender, DISPUTE_WINDOW, leaf.hash) .await .map_err(JfyiError::RollingSessionWindow)?, ))) @@ -378,11 +365,8 @@ where } /// Wait for `ActiveLeavesUpdate`, returns `None` if `Conclude` signal came first. -async fn wait_for_first_leaf(ctx: &mut Context) -> Result> -where - Context: overseer::SubsystemContext, - Context: SubsystemContext, -{ +#[overseer::contextbounds(DisputeCoordinator, prefix = self::overseer)] +async fn wait_for_first_leaf(ctx: &mut Context) -> Result> { loop { match ctx.recv().await? { FromOverseer::Signal(OverseerSignal::Conclude) => return Ok(None), diff --git a/node/core/dispute-coordinator/src/participation/mod.rs b/node/core/dispute-coordinator/src/participation/mod.rs index 6c47f41cc603..3c066641d9f4 100644 --- a/node/core/dispute-coordinator/src/participation/mod.rs +++ b/node/core/dispute-coordinator/src/participation/mod.rs @@ -28,7 +28,7 @@ use futures_timer::Delay; use polkadot_node_primitives::{ValidationResult, APPROVAL_EXECUTION_TIMEOUT}; use polkadot_node_subsystem::{ messages::{AvailabilityRecoveryMessage, AvailabilityStoreMessage, CandidateValidationMessage}, - ActiveLeavesUpdate, RecoveryError, SubsystemContext, SubsystemSender, + overseer, ActiveLeavesUpdate, RecoveryError, }; use polkadot_node_subsystem_util::runtime::get_validation_code_by_hash; use polkadot_primitives::v2::{BlockNumber, CandidateHash, CandidateReceipt, Hash, SessionIndex}; @@ -123,6 +123,7 @@ impl WorkerMessage { } } +#[overseer::contextbounds(DisputeCoordinator, prefix = self::overseer)] impl Participation { /// Get ready for managing dispute participation requests. /// @@ -144,7 +145,7 @@ impl Participation { /// `on_active_leaves_update`, the participation will be launched right away. /// /// Returns: false, if queues are already full. - pub async fn queue_participation( + pub async fn queue_participation( &mut self, ctx: &mut Context, priority: ParticipationPriority, @@ -174,7 +175,7 @@ impl Participation { /// /// Returns: The received `ParticipationStatement` or a fatal error, in case /// something went wrong when dequeuing more requests (tasks could not be spawned). - pub async fn get_participation_result( + pub async fn get_participation_result( &mut self, ctx: &mut Context, msg: WorkerMessage, @@ -190,7 +191,7 @@ impl Participation { /// /// Make sure we to dequeue participations if that became possible and update most recent /// block. - pub async fn process_active_leaves_update( + pub async fn process_active_leaves_update( &mut self, ctx: &mut Context, update: &ActiveLeavesUpdate, @@ -212,7 +213,8 @@ impl Participation { } /// Dequeue until `MAX_PARALLEL_PARTICIPATIONS` is reached. - async fn dequeue_until_capacity( + + async fn dequeue_until_capacity( &mut self, ctx: &mut Context, recent_head: Hash, @@ -228,7 +230,7 @@ impl Participation { } /// Fork a participation task in the background. - fn fork_participation( + fn fork_participation( &mut self, ctx: &mut Context, req: ParticipationRequest, @@ -248,7 +250,7 @@ impl Participation { async fn participate( mut result_sender: WorkerMessageSender, - mut sender: impl SubsystemSender, + mut sender: impl overseer::DisputeCoordinatorSenderTrait, block_hash: Hash, req: ParticipationRequest, ) { @@ -259,15 +261,12 @@ async fn participate( // available data let (recover_available_data_tx, recover_available_data_rx) = oneshot::channel(); sender - .send_message( - AvailabilityRecoveryMessage::RecoverAvailableData( - req.candidate_receipt().clone(), - req.session(), - None, - recover_available_data_tx, - ) - .into(), - ) + .send_message(AvailabilityRecoveryMessage::RecoverAvailableData( + req.candidate_receipt().clone(), + req.session(), + None, + recover_available_data_tx, + )) .await; let available_data = match recover_available_data_rx.await { @@ -326,15 +325,12 @@ async fn participate( // in the dispute let (store_available_data_tx, store_available_data_rx) = oneshot::channel(); sender - .send_message( - AvailabilityStoreMessage::StoreAvailableData { - candidate_hash: *req.candidate_hash(), - n_validators: req.n_validators() as u32, - available_data: available_data.clone(), - tx: store_available_data_tx, - } - .into(), - ) + .send_message(AvailabilityStoreMessage::StoreAvailableData { + candidate_hash: *req.candidate_hash(), + n_validators: req.n_validators() as u32, + available_data: available_data.clone(), + tx: store_available_data_tx, + }) .await; match store_available_data_rx.await { @@ -364,17 +360,14 @@ async fn participate( // same level of leeway. let (validation_tx, validation_rx) = oneshot::channel(); sender - .send_message( - CandidateValidationMessage::ValidateFromExhaustive( - available_data.validation_data, - validation_code, - req.candidate_receipt().clone(), - available_data.pov, - APPROVAL_EXECUTION_TIMEOUT, - validation_tx, - ) - .into(), - ) + .send_message(CandidateValidationMessage::ValidateFromExhaustive( + available_data.validation_data, + validation_code, + req.candidate_receipt().clone(), + available_data.pov, + APPROVAL_EXECUTION_TIMEOUT, + validation_tx, + )) .await; // we cast votes (either positive or negative) depending on the outcome of diff --git a/node/core/dispute-coordinator/src/participation/queues/mod.rs b/node/core/dispute-coordinator/src/participation/queues/mod.rs index 587f0bf10760..158bcfd2d3b6 100644 --- a/node/core/dispute-coordinator/src/participation/queues/mod.rs +++ b/node/core/dispute-coordinator/src/participation/queues/mod.rs @@ -20,7 +20,7 @@ use std::{ }; use futures::channel::oneshot; -use polkadot_node_subsystem::{messages::ChainApiMessage, SubsystemSender}; +use polkadot_node_subsystem::{messages::ChainApiMessage, overseer}; use polkadot_primitives::v2::{BlockNumber, CandidateHash, CandidateReceipt, Hash, SessionIndex}; use crate::{ @@ -163,7 +163,7 @@ impl Queues { /// Returns error in case a queue was found full already. pub async fn queue( &mut self, - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::DisputeCoordinatorSenderTrait, priority: ParticipationPriority, req: ParticipationRequest, ) -> Result<()> { @@ -305,7 +305,7 @@ impl CandidateComparator { /// `Ok(None)` in case we could not lookup the candidate's relay parent, returns a /// `FatalError` in case the chain API call fails with an unexpected error. pub async fn new( - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::DisputeCoordinatorSenderTrait, candidate: &CandidateReceipt, ) -> FatalResult> { let candidate_hash = candidate.hash(); @@ -350,11 +350,11 @@ impl Ord for CandidateComparator { } async fn get_block_number( - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::DisputeCoordinatorSenderTrait, relay_parent: Hash, ) -> FatalResult> { let (tx, rx) = oneshot::channel(); - sender.send_message(ChainApiMessage::BlockNumber(relay_parent, tx).into()).await; + sender.send_message(ChainApiMessage::BlockNumber(relay_parent, tx)).await; rx.await .map_err(|_| FatalError::ChainApiSenderDropped)? .map_err(FatalError::ChainApiAncestors) diff --git a/node/core/dispute-coordinator/src/participation/tests.rs b/node/core/dispute-coordinator/src/participation/tests.rs index 7fa635482004..37400f365fb1 100644 --- a/node/core/dispute-coordinator/src/participation/tests.rs +++ b/node/core/dispute-coordinator/src/participation/tests.rs @@ -53,16 +53,15 @@ pub fn make_our_subsystem_context( make_subsystem_context(spawn) } -async fn participate( - ctx: &mut impl SubsystemContext, - participation: &mut Participation, -) -> Result<()> { +#[overseer::contextbounds(DisputeCoordinator, prefix = self::overseer)] +async fn participate(ctx: &mut Context, participation: &mut Participation) -> Result<()> { let commitments = CandidateCommitments::default(); participate_with_commitments_hash(ctx, participation, commitments.hash()).await } -async fn participate_with_commitments_hash( - ctx: &mut impl SubsystemContext, +#[overseer::contextbounds(DisputeCoordinator, prefix = self::overseer)] +async fn participate_with_commitments_hash( + ctx: &mut Context, participation: &mut Participation, commitments_hash: Hash, ) -> Result<()> { @@ -81,8 +80,9 @@ async fn participate_with_commitments_hash( .await } -async fn activate_leaf( - ctx: &mut impl SubsystemContext, +#[overseer::contextbounds(DisputeCoordinator, prefix = self::overseer)] +async fn activate_leaf( + ctx: &mut Context, participation: &mut Participation, block_number: BlockNumber, ) -> FatalResult<()> { diff --git a/node/core/dispute-coordinator/src/scraping/mod.rs b/node/core/dispute-coordinator/src/scraping/mod.rs index 060eaecc0414..6fcae077ad8a 100644 --- a/node/core/dispute-coordinator/src/scraping/mod.rs +++ b/node/core/dispute-coordinator/src/scraping/mod.rs @@ -21,7 +21,8 @@ use lru::LruCache; use polkadot_node_primitives::MAX_FINALITY_LAG; use polkadot_node_subsystem::{ - messages::ChainApiMessage, ActivatedLeaf, ActiveLeavesUpdate, ChainApiError, SubsystemSender, + messages::ChainApiMessage, overseer, ActivatedLeaf, ActiveLeavesUpdate, ChainApiError, + SubsystemSender, }; use polkadot_node_subsystem_util::runtime::{get_candidate_events, get_on_chain_votes}; use polkadot_primitives::v2::{ @@ -81,10 +82,13 @@ impl ChainScraper { /// Create a properly initialized `OrderingProvider`. /// /// Returns: `Self` and any scraped votes. - pub async fn new( + pub async fn new( sender: &mut Sender, initial_head: ActivatedLeaf, - ) -> Result<(Self, Vec)> { + ) -> Result<(Self, Vec)> + where + Sender: overseer::DisputeCoordinatorSenderTrait, + { let mut s = Self { included_candidates: HashSet::new(), candidates_by_block_number: BTreeMap::new(), @@ -106,11 +110,14 @@ impl ChainScraper { /// and updates current heads, so we can query candidates for all non finalized blocks. /// /// Returns: On chain vote for the leaf and any ancestors we might not yet have seen. - pub async fn process_active_leaves_update( + pub async fn process_active_leaves_update( &mut self, sender: &mut Sender, update: &ActiveLeavesUpdate, - ) -> crate::error::Result> { + ) -> crate::error::Result> + where + Sender: overseer::DisputeCoordinatorSenderTrait, + { let activated = match update.activated.as_ref() { Some(activated) => activated, None => return Ok(Vec::new()), @@ -160,12 +167,15 @@ impl ChainScraper { /// Process candidate events of a block. /// /// Keep track of all included candidates. - async fn process_candidate_events( + async fn process_candidate_events( &mut self, - sender: &mut impl SubsystemSender, + sender: &mut Sender, block_number: BlockNumber, block_hash: Hash, - ) -> Result<()> { + ) -> Result<()> + where + Sender: overseer::DisputeCoordinatorSenderTrait, + { // Get included events: let included = get_candidate_events(sender, block_hash) @@ -196,12 +206,15 @@ impl ChainScraper { /// either at the block present in cache or at the last finalized block. /// /// Both `head` and the latest finalized block are **not** included in the result. - async fn get_unfinalized_block_ancestors( + async fn get_unfinalized_block_ancestors( &mut self, sender: &mut Sender, mut head: Hash, mut head_number: BlockNumber, - ) -> Result> { + ) -> Result> + where + Sender: overseer::DisputeCoordinatorSenderTrait, + { let target_ancestor = get_finalized_block_number(sender).await?; let mut ancestors = Vec::new(); @@ -256,26 +269,29 @@ impl ChainScraper { } } -async fn get_finalized_block_number(sender: &mut impl SubsystemSender) -> FatalResult { +async fn get_finalized_block_number(sender: &mut Sender) -> FatalResult +where + Sender: overseer::DisputeCoordinatorSenderTrait, +{ let (number_tx, number_rx) = oneshot::channel(); send_message_fatal(sender, ChainApiMessage::FinalizedBlockNumber(number_tx), number_rx).await } -async fn get_block_ancestors( - sender: &mut impl SubsystemSender, +async fn get_block_ancestors( + sender: &mut Sender, head: Hash, num_ancestors: BlockNumber, -) -> FatalResult> { +) -> FatalResult> +where + Sender: overseer::DisputeCoordinatorSenderTrait, +{ let (tx, rx) = oneshot::channel(); sender - .send_message( - ChainApiMessage::Ancestors { - hash: head, - k: num_ancestors as usize, - response_channel: tx, - } - .into(), - ) + .send_message(ChainApiMessage::Ancestors { + hash: head, + k: num_ancestors as usize, + response_channel: tx, + }) .await; rx.await @@ -289,9 +305,9 @@ async fn send_message_fatal( receiver: oneshot::Receiver>, ) -> FatalResult where - Sender: SubsystemSender, + Sender: SubsystemSender, { - sender.send_message(message.into()).await; + sender.send_message(message).await; receiver .await diff --git a/node/core/provisioner/src/lib.rs b/node/core/provisioner/src/lib.rs index 4a33655274d8..afbdf458746e 100644 --- a/node/core/provisioner/src/lib.rs +++ b/node/core/provisioner/src/lib.rs @@ -32,7 +32,7 @@ use polkadot_node_subsystem::{ CandidateBackingMessage, ChainApiMessage, DisputeCoordinatorMessage, ProvisionableData, ProvisionerInherentData, ProvisionerMessage, }, - ActivatedLeaf, LeafStatus, PerLeafSpan, SubsystemSender, + overseer, ActivatedLeaf, LeafStatus, PerLeafSpan, }; use polkadot_node_subsystem_util::{ request_availability_cores, request_persisted_validation_data, JobSender, JobSubsystem, @@ -95,8 +95,12 @@ impl InherentAfter { } } +/// Provisioner run arguments. +#[derive(Debug, Clone, Copy)] +pub struct ProvisionerConfig; + /// A per-relay-parent job for the provisioning subsystem. -pub struct ProvisionerJob { +pub struct ProvisionerJob { leaf: ActivatedLeaf, receiver: mpsc::Receiver, backed_candidates: Vec, @@ -104,14 +108,16 @@ pub struct ProvisionerJob { metrics: Metrics, inherent_after: InherentAfter, awaiting_inherent: Vec>, + _phantom: std::marker::PhantomData, } -/// Provisioner run arguments. -#[derive(Debug, Clone, Copy)] -pub struct ProvisionerConfig; - -impl JobTrait for ProvisionerJob { +impl JobTrait for ProvisionerJob +where + Sender: overseer::ProvisionerSenderTrait + std::marker::Unpin, +{ type ToJob = ProvisionerMessage; + type OutgoingMessages = overseer::ProvisionerOutgoingMessages; + type Sender = Sender; type Error = Error; type RunArgs = ProvisionerConfig; type Metrics = Metrics; @@ -121,12 +127,12 @@ impl JobTrait for ProvisionerJob { /// Run a job for the parent block indicated // // this function is in charge of creating and executing the job's main loop - fn run( + fn run( leaf: ActivatedLeaf, _: Self::RunArgs, metrics: Self::Metrics, receiver: mpsc::Receiver, - mut sender: JobSender, + mut sender: JobSender, ) -> Pin> + Send>> { let span = leaf.span.clone(); async move { @@ -139,7 +145,10 @@ impl JobTrait for ProvisionerJob { } } -impl ProvisionerJob { +impl ProvisionerJob +where + Sender: overseer::ProvisionerSenderTrait, +{ fn new( leaf: ActivatedLeaf, metrics: Metrics, @@ -153,14 +162,11 @@ impl ProvisionerJob { metrics, inherent_after: InherentAfter::new_from_now(), awaiting_inherent: Vec::new(), + _phantom: std::marker::PhantomData::::default(), } } - async fn run_loop( - mut self, - sender: &mut impl SubsystemSender, - span: PerLeafSpan, - ) -> Result<(), Error> { + async fn run_loop(mut self, sender: &mut Sender, span: PerLeafSpan) -> Result<(), Error> { loop { futures::select! { msg = self.receiver.next() => match msg { @@ -197,7 +203,7 @@ impl ProvisionerJob { async fn send_inherent_data( &mut self, - sender: &mut impl SubsystemSender, + sender: &mut Sender, return_senders: Vec>, ) { if let Err(err) = send_inherent_data( @@ -275,7 +281,7 @@ async fn send_inherent_data( bitfields: &[SignedAvailabilityBitfield], candidates: &[CandidateReceipt], return_senders: Vec>, - from_job: &mut impl SubsystemSender, + from_job: &mut impl overseer::ProvisionerSenderTrait, metrics: &Metrics, ) -> Result<(), Error> { let availability_cores = request_availability_cores(leaf.hash, from_job) @@ -394,7 +400,7 @@ async fn select_candidates( bitfields: &[SignedAvailabilityBitfield], candidates: &[CandidateReceipt], relay_parent: Hash, - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::ProvisionerSenderTrait, ) -> Result, Error> { let block_number = get_block_number_under_construction(relay_parent, sender).await?; @@ -472,14 +478,11 @@ async fn select_candidates( // now get the backed candidates corresponding to these candidate receipts let (tx, rx) = oneshot::channel(); sender - .send_message( - CandidateBackingMessage::GetBackedCandidates( - relay_parent, - selected_candidates.clone(), - tx, - ) - .into(), - ) + .send_message(CandidateBackingMessage::GetBackedCandidates( + relay_parent, + selected_candidates.clone(), + tx, + )) .await; let mut candidates = rx.await.map_err(|err| Error::CanceledBackedCandidates(err))?; @@ -530,10 +533,10 @@ async fn select_candidates( /// in the event of an invalid `relay_parent`, returns `Ok(0)` async fn get_block_number_under_construction( relay_parent: Hash, - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::ProvisionerSenderTrait, ) -> Result { let (tx, rx) = oneshot::channel(); - sender.send_message(ChainApiMessage::BlockNumber(relay_parent, tx).into()).await; + sender.send_message(ChainApiMessage::BlockNumber(relay_parent, tx)).await; match rx.await.map_err(|err| Error::CanceledBlockNumber(err))? { Ok(Some(n)) => Ok(n + 1), @@ -591,7 +594,7 @@ enum RequestType { /// Request open disputes identified by `CandidateHash` and the `SessionIndex`. async fn request_disputes( - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::ProvisionerSenderTrait, active_or_recent: RequestType, ) -> Vec<(SessionIndex, CandidateHash)> { let (tx, rx) = oneshot::channel(); @@ -600,7 +603,7 @@ async fn request_disputes( RequestType::Active => DisputeCoordinatorMessage::ActiveDisputes(tx), }; // Bounded by block production - `ProvisionerMessage::RequestInherentData`. - sender.send_unbounded_message(msg.into()); + sender.send_unbounded_message(msg); let recent_disputes = match rx.await { Ok(r) => r, @@ -614,14 +617,15 @@ async fn request_disputes( /// Request the relevant dispute statements for a set of disputes identified by `CandidateHash` and the `SessionIndex`. async fn request_votes( - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::ProvisionerSenderTrait, disputes_to_query: Vec<(SessionIndex, CandidateHash)>, ) -> Vec<(SessionIndex, CandidateHash, CandidateVotes)> { let (tx, rx) = oneshot::channel(); // Bounded by block production - `ProvisionerMessage::RequestInherentData`. - sender.send_unbounded_message( - DisputeCoordinatorMessage::QueryCandidateVotes(disputes_to_query, tx).into(), - ); + sender.send_unbounded_message(DisputeCoordinatorMessage::QueryCandidateVotes( + disputes_to_query, + tx, + )); match rx.await { Ok(v) => v, @@ -665,7 +669,7 @@ fn extend_by_random_subset_without_repetition( const MAX_DISPUTES_FORWARDED_TO_RUNTIME: usize = 1_000; async fn select_disputes( - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::ProvisionerSenderTrait, metrics: &metrics::Metrics, _leaf: &ActivatedLeaf, ) -> Result { @@ -804,4 +808,4 @@ async fn select_disputes( } /// The provisioner subsystem. -pub type ProvisionerSubsystem = JobSubsystem; +pub type ProvisionerSubsystem = JobSubsystem, Spawner>; diff --git a/node/core/provisioner/src/onchain_disputes.rs b/node/core/provisioner/src/onchain_disputes.rs index e5b736b600b3..6c83d77fc9c9 100644 --- a/node/core/provisioner/src/onchain_disputes.rs +++ b/node/core/provisioner/src/onchain_disputes.rs @@ -15,14 +15,17 @@ // along with Polkadot. If not, see . use crate::error::GetOnchainDisputesError; -use polkadot_node_subsystem::SubsystemSender; +use polkadot_node_subsystem::overseer; use polkadot_primitives::v2::{CandidateHash, DisputeState, Hash, SessionIndex}; use std::collections::HashMap; -pub async fn get_onchain_disputes( - _sender: &mut impl SubsystemSender, +pub async fn get_onchain_disputes( + _sender: &mut Sender, _relay_parent: Hash, -) -> Result, GetOnchainDisputesError> { +) -> Result, GetOnchainDisputesError> +where + Sender: overseer::ProvisionerSenderTrait, +{ let _onchain = Result::< HashMap<(SessionIndex, CandidateHash), DisputeState>, GetOnchainDisputesError, @@ -46,8 +49,8 @@ mod staging_impl { }; /// Gets the on-chain disputes at a given block number and returns them as a `HashSet` so that searching in them is cheap. - pub async fn get_onchain_disputes( - sender: &mut impl SubsystemSender, + pub async fn get_onchain_disputes( + sender: &mut Sender, relay_parent: Hash, ) -> Result, GetOnchainDisputesError> { gum::trace!(target: LOG_TARGET, ?relay_parent, "Fetching on-chain disputes"); diff --git a/node/core/pvf-checker/src/lib.rs b/node/core/pvf-checker/src/lib.rs index 5d6f544e39ae..e5ace4c8f7ef 100644 --- a/node/core/pvf-checker/src/lib.rs +++ b/node/core/pvf-checker/src/lib.rs @@ -22,9 +22,9 @@ use futures::{channel::oneshot, future::BoxFuture, prelude::*, stream::FuturesUnordered}; use polkadot_node_subsystem::{ - messages::{CandidateValidationMessage, PreCheckOutcome, PvfCheckerMessage}, - overseer, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemContext, - SubsystemError, SubsystemResult, SubsystemSender, + messages::{CandidateValidationMessage, PreCheckOutcome, PvfCheckerMessage, RuntimeApiMessage}, + overseer, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemError, + SubsystemResult, SubsystemSender, }; use polkadot_primitives::v2::{ BlockNumber, Hash, PvfCheckStatement, SessionIndex, ValidationCodeHash, ValidatorId, @@ -60,11 +60,8 @@ impl PvfCheckerSubsystem { } } -impl overseer::Subsystem for PvfCheckerSubsystem -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::subsystem(PvfChecker, error=SubsystemError, prefix = self::overseer)] +impl PvfCheckerSubsystem { fn start(self, ctx: Context) -> SpawnedSubsystem { if self.enabled { let future = run(ctx, self.keystore, self.metrics) @@ -123,15 +120,12 @@ struct State { FuturesUnordered>>, } +#[overseer::contextbounds(PvfChecker, prefix = self::overseer)] async fn run( mut ctx: Context, keystore: SyncCryptoStorePtr, metrics: Metrics, -) -> SubsystemResult<()> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> SubsystemResult<()> { let mut state = State { credentials: None, recent_block: None, @@ -179,7 +173,7 @@ where /// Handle an incoming PVF pre-check result from the candidate-validation subsystem. async fn handle_pvf_check( state: &mut State, - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::PvfCheckerSenderTrait, keystore: &SyncCryptoStorePtr, metrics: &Metrics, outcome: PreCheckOutcome, @@ -247,7 +241,7 @@ struct Conclude; async fn handle_from_overseer( state: &mut State, - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::PvfCheckerSenderTrait, keystore: &SyncCryptoStorePtr, metrics: &Metrics, from_overseer: FromOverseer, @@ -273,7 +267,7 @@ async fn handle_from_overseer( async fn handle_leaves_update( state: &mut State, - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::PvfCheckerSenderTrait, keystore: &SyncCryptoStorePtr, metrics: &Metrics, update: ActiveLeavesUpdate, @@ -355,7 +349,7 @@ struct ActivationEffect { /// Returns `None` if the PVF pre-checking runtime API is not supported for the given leaf hash. async fn examine_activation( state: &mut State, - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::PvfCheckerSenderTrait, keystore: &SyncCryptoStorePtr, leaf_hash: Hash, leaf_number: BlockNumber, @@ -414,7 +408,7 @@ async fn examine_activation( /// Checks the active validators for the given leaf. If we have a signing key for one of them, /// returns the [`SigningCredentials`]. async fn check_signing_credentials( - sender: &mut impl SubsystemSender, + sender: &mut impl SubsystemSender, keystore: &SyncCryptoStorePtr, leaf: Hash, ) -> Option { @@ -443,7 +437,7 @@ async fn check_signing_credentials( /// /// If the validator already voted for the given code, this function does nothing. async fn sign_and_submit_pvf_check_statement( - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::PvfCheckerSenderTrait, keystore: &SyncCryptoStorePtr, voted: &mut HashSet, credentials: &SigningCredentials, @@ -535,7 +529,7 @@ async fn sign_and_submit_pvf_check_statement( /// into the `currently_checking` set. async fn initiate_precheck( state: &mut State, - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::PvfCheckerSenderTrait, relay_parent: Hash, validation_code_hash: ValidationCodeHash, metrics: &Metrics, @@ -544,9 +538,7 @@ async fn initiate_precheck( let (tx, rx) = oneshot::channel(); sender - .send_message( - CandidateValidationMessage::PreCheck(relay_parent, validation_code_hash, tx).into(), - ) + .send_message(CandidateValidationMessage::PreCheck(relay_parent, validation_code_hash, tx)) .await; let timer = metrics.time_pre_check_judgement(); diff --git a/node/core/pvf-checker/src/runtime_api.rs b/node/core/pvf-checker/src/runtime_api.rs index 798fb3f1abfb..ccdcce4c2c83 100644 --- a/node/core/pvf-checker/src/runtime_api.rs +++ b/node/core/pvf-checker/src/runtime_api.rs @@ -26,7 +26,7 @@ use polkadot_primitives::v2::{ }; pub(crate) async fn session_index_for_child( - sender: &mut impl SubsystemSender, + sender: &mut impl SubsystemSender, relay_parent: Hash, ) -> Result { let (tx, rx) = oneshot::channel(); @@ -34,7 +34,7 @@ pub(crate) async fn session_index_for_child( } pub(crate) async fn validators( - sender: &mut impl SubsystemSender, + sender: &mut impl SubsystemSender, relay_parent: Hash, ) -> Result, RuntimeRequestError> { let (tx, rx) = oneshot::channel(); @@ -42,7 +42,7 @@ pub(crate) async fn validators( } pub(crate) async fn submit_pvf_check_statement( - sender: &mut impl SubsystemSender, + sender: &mut impl SubsystemSender, relay_parent: Hash, stmt: PvfCheckStatement, signature: ValidatorSignature, @@ -58,7 +58,7 @@ pub(crate) async fn submit_pvf_check_statement( } pub(crate) async fn pvfs_require_precheck( - sender: &mut impl SubsystemSender, + sender: &mut impl SubsystemSender, relay_parent: Hash, ) -> Result, RuntimeRequestError> { let (tx, rx) = oneshot::channel(); @@ -73,7 +73,7 @@ pub(crate) enum RuntimeRequestError { } pub(crate) async fn runtime_api_request( - sender: &mut impl SubsystemSender, + sender: &mut impl SubsystemSender, relay_parent: Hash, request: RuntimeApiRequest, receiver: oneshot::Receiver>, diff --git a/node/core/runtime-api/src/lib.rs b/node/core/runtime-api/src/lib.rs index fa20a2ad768a..38f01da13989 100644 --- a/node/core/runtime-api/src/lib.rs +++ b/node/core/runtime-api/src/lib.rs @@ -25,8 +25,7 @@ use polkadot_node_subsystem::{ errors::RuntimeApiError, messages::{RuntimeApiMessage, RuntimeApiRequest as Request}, - overseer, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemContext, SubsystemError, - SubsystemResult, + overseer, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemError, SubsystemResult, }; use polkadot_primitives::{ runtime_api::ParachainHost, @@ -92,12 +91,11 @@ impl RuntimeApiSubsystem { } } -impl overseer::Subsystem for RuntimeApiSubsystem +#[overseer::subsystem(RuntimeApi, error = SubsystemError, prefix = self::overseer)] +impl RuntimeApiSubsystem where Client: ProvideRuntimeApi + Send + 'static + Sync, Client::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, - Context: SubsystemContext, - Context: overseer::SubsystemContext, { fn start(self, ctx: Context) -> SpawnedSubsystem { SpawnedSubsystem { future: run(ctx, self).boxed(), name: "runtime-api-subsystem" } @@ -333,6 +331,7 @@ where } } +#[overseer::contextbounds(RuntimeApi, prefix = self::overseer)] async fn run( mut ctx: Context, mut subsystem: RuntimeApiSubsystem, @@ -340,8 +339,6 @@ async fn run( where Client: ProvideRuntimeApi + Send + Sync + 'static, Client::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, - Context: SubsystemContext, - Context: overseer::SubsystemContext, { loop { select! { diff --git a/node/malus/src/interceptor.rs b/node/malus/src/interceptor.rs index 5da91fd42b66..19889b42e32a 100644 --- a/node/malus/src/interceptor.rs +++ b/node/malus/src/interceptor.rs @@ -21,16 +21,18 @@ //! messages on the overseer level. use polkadot_node_subsystem::*; -pub use polkadot_node_subsystem::{messages::AllMessages, overseer, FromOverseer}; +pub use polkadot_node_subsystem::{messages, messages::*, overseer, FromOverseer}; use std::{future::Future, pin::Pin}; /// Filter incoming and outgoing messages. pub trait MessageInterceptor: Send + Sync + Clone + 'static where - Sender: overseer::SubsystemSender + Clone + 'static, + Sender: overseer::SubsystemSender<::OutgoingMessages> + + Clone + + 'static, { /// The message type the original subsystem handles incoming. - type Message: Send + 'static; + type Message: overseer::AssociateOutgoing + Send + 'static; /// Filter messages that are to be received by /// the subsystem. @@ -46,7 +48,10 @@ where } /// Modify outgoing messages. - fn intercept_outgoing(&self, msg: AllMessages) -> Option { + fn intercept_outgoing( + &self, + msg: ::OutgoingMessages, + ) -> Option<::OutgoingMessages> { Some(msg) } } @@ -59,13 +64,26 @@ pub struct InterceptedSender { } #[async_trait::async_trait] -impl overseer::SubsystemSender for InterceptedSender +impl overseer::SubsystemSender for InterceptedSender where - Sender: overseer::SubsystemSender - + overseer::SubsystemSender<>::Message>, + OutgoingMessage: overseer::AssociateOutgoing + Send + 'static, + Sender: overseer::SubsystemSender + + overseer::SubsystemSender< + < + >::Message as overseer::AssociateOutgoing + >::OutgoingMessages + >, Fil: MessageInterceptor, + >::Message: overseer::AssociateOutgoing, + < + >::Message as overseer::AssociateOutgoing + >::OutgoingMessages: + From, { - async fn send_message(&mut self, msg: AllMessages) { + async fn send_message(&mut self, msg: OutgoingMessage) { + let msg = < + <>::Message as overseer::AssociateOutgoing + >::OutgoingMessages as From>::from(msg); if let Some(msg) = self.message_filter.intercept_outgoing(msg) { self.inner.send_message(msg).await; } @@ -73,7 +91,7 @@ where async fn send_messages(&mut self, msgs: T) where - T: IntoIterator + Send, + T: IntoIterator + Send, T::IntoIter: Send, { for msg in msgs { @@ -81,7 +99,10 @@ where } } - fn send_unbounded_message(&mut self, msg: AllMessages) { + fn send_unbounded_message(&mut self, msg: OutgoingMessage) { + let msg = < + <>::Message as overseer::AssociateOutgoing + >::OutgoingMessages as From>::from(msg); if let Some(msg) = self.message_filter.intercept_outgoing(msg) { self.inner.send_unbounded_message(msg); } @@ -91,11 +112,16 @@ where /// A subsystem context, that filters the outgoing messages. pub struct InterceptedContext where - Context: overseer::SubsystemContext + SubsystemContext, + Context: overseer::SubsystemContext, Fil: MessageInterceptor<::Sender>, - ::Sender: overseer::SubsystemSender< - ::Sender>>::Message, - >, + ::Sender: + overseer::SubsystemSender< + < + < + Fil as MessageInterceptor<::Sender> + >::Message as overseer::AssociateOutgoing + >::OutgoingMessages, + >, { inner: Context, message_filter: Fil, @@ -104,14 +130,15 @@ where impl InterceptedContext where - Context: overseer::SubsystemContext + SubsystemContext, + Context: overseer::SubsystemContext, Fil: MessageInterceptor< ::Sender, Message = ::Message, >, + ::Message: overseer::AssociateOutgoing, ::Sender: overseer::SubsystemSender< - ::Sender>>::Message, - >, + <::Message as overseer::AssociateOutgoing>::OutgoingMessages + > { pub fn new(mut inner: Context, message_filter: Fil) -> Self { let sender = InterceptedSender::<::Sender, Fil> { @@ -125,22 +152,27 @@ where #[async_trait::async_trait] impl overseer::SubsystemContext for InterceptedContext where - Context: overseer::SubsystemContext + SubsystemContext, + Context: overseer::SubsystemContext, + ::Message: + overseer::AssociateOutgoing, + ::Sender: + overseer::SubsystemSender< + <::Message as overseer::AssociateOutgoing>::OutgoingMessages + >, + InterceptedSender<::Sender, Fil>: + overseer::SubsystemSender< + <::Message as overseer::AssociateOutgoing>::OutgoingMessages + >, Fil: MessageInterceptor< ::Sender, Message = ::Message, >, - ::AllMessages: - From<::Message>, - ::Sender: overseer::SubsystemSender< - ::Sender>>::Message, - >, { type Message = ::Message; type Sender = InterceptedSender<::Sender, Fil>; - type Error = ::Error; - type AllMessages = ::AllMessages; - type Signal = ::Signal; + type Error = SubsystemError; + type OutgoingMessages = <::Message as overseer::AssociateOutgoing>::OutgoingMessages; + type Signal = OverseerSignal; async fn try_recv(&mut self) -> Result>, ()> { loop { @@ -200,16 +232,28 @@ impl InterceptedSubsystem { impl overseer::Subsystem for InterceptedSubsystem where - Context: overseer::SubsystemContext + SubsystemContext + Sync + Send, - Sub: overseer::Subsystem, SubsystemError>, - InterceptedContext: overseer::SubsystemContext + SubsystemContext, - Interceptor: MessageInterceptor< - ::Sender, - Message = ::Message, - >, - ::Sender: overseer::SubsystemSender< - ::Sender>>::Message, - >, + Context: + overseer::SubsystemContext + Sync + Send, + InterceptedContext: + overseer::SubsystemContext, + Sub: + overseer::Subsystem, SubsystemError>, + Interceptor: + MessageInterceptor< + ::Sender, + Message = ::Message, + >, + // ::Sender: + // overseer::SubsystemSender< + // ::Sender>>::Message, + // >, + ::Message: + overseer::AssociateOutgoing, + ::Sender: + overseer::SubsystemSender< + <::Message as overseer::AssociateOutgoing + >::OutgoingMessages + >, { fn start(self, ctx: Context) -> SpawnedSubsystem { let ctx = InterceptedContext::new(ctx, self.message_interceptor); diff --git a/node/malus/src/tests.rs b/node/malus/src/tests.rs index 57072336852b..6ce65fd9fc3b 100644 --- a/node/malus/src/tests.rs +++ b/node/malus/src/tests.rs @@ -19,8 +19,8 @@ use super::*; use polkadot_node_subsystem_test_helpers::*; use polkadot_node_subsystem::{ - messages::{AllMessages, AvailabilityStoreMessage}, - overseer::{dummy::DummySubsystem, gen::TimeoutExt, Subsystem}, + messages::AvailabilityStoreMessage, + overseer::{dummy::DummySubsystem, gen::TimeoutExt, Subsystem, AssociateOutgoing}, SubsystemError, }; @@ -29,8 +29,7 @@ struct BlackHoleInterceptor; impl MessageInterceptor for BlackHoleInterceptor where - Sender: overseer::SubsystemSender - + overseer::SubsystemSender + Sender: overseer::AvailabilityStoreSenderTrait + Clone + 'static, { @@ -53,8 +52,7 @@ struct PassInterceptor; impl MessageInterceptor for PassInterceptor where - Sender: overseer::SubsystemSender - + overseer::SubsystemSender + Sender: overseer::AvailabilityStoreSenderTrait + Clone + 'static, { @@ -68,8 +66,8 @@ async fn overseer_send>(overseer: &mut TestSubsystemContext fn launch_harness(test_gen: G) where F: Future> + Send, - M: Into + std::fmt::Debug + Send + 'static, - AllMessages: From, + M: AssociateOutgoing + std::fmt::Debug + Send + 'static, + // ::OutgoingMessages: From, Sub: Subsystem, SubsystemError>, G: Fn(TestSubsystemContextHandle) -> (F, Sub), { diff --git a/node/malus/src/variants/common.rs b/node/malus/src/variants/common.rs index 8bed137126d1..138a91b759ab 100644 --- a/node/malus/src/variants/common.rs +++ b/node/malus/src/variants/common.rs @@ -131,11 +131,7 @@ where subsystem_sender: Sender, response_sender: oneshot::Sender>, ) where - Sender: overseer::SubsystemSender - + overseer::SubsystemSender - + Clone - + Send - + 'static, + Sender: overseer::CandidateValidationSenderTrait + Clone + Send + 'static, { let _candidate_descriptor = candidate_descriptor.clone(); let mut subsystem_sender = subsystem_sender.clone(); @@ -200,11 +196,7 @@ fn create_validation_response( impl MessageInterceptor for ReplaceValidationResult where - Sender: overseer::SubsystemSender - + overseer::SubsystemSender - + Clone - + Send - + 'static, + Sender: overseer::CandidateValidationSenderTrait + Clone + Send + 'static, Spawner: SpawnNamed + Clone + 'static, { type Message = CandidateValidationMessage; @@ -336,7 +328,10 @@ where } } - fn intercept_outgoing(&self, msg: AllMessages) -> Option { + fn intercept_outgoing( + &self, + msg: overseer::CandidateValidationOutgoingMessages, + ) -> Option { Some(msg) } } diff --git a/node/malus/src/variants/suggest_garbage_candidate.rs b/node/malus/src/variants/suggest_garbage_candidate.rs index af8191fe10a5..ef987278decb 100644 --- a/node/malus/src/variants/suggest_garbage_candidate.rs +++ b/node/malus/src/variants/suggest_garbage_candidate.rs @@ -71,11 +71,7 @@ struct NoteCandidate { impl MessageInterceptor for NoteCandidate where - Sender: overseer::SubsystemSender - + overseer::SubsystemSender - + Clone - + Send - + 'static, + Sender: overseer::CandidateBackingSenderTrait + Clone + Send + 'static, Spawner: SpawnNamed + Clone + 'static, { type Message = CandidateBackingMessage; @@ -219,20 +215,21 @@ where } } - fn intercept_outgoing(&self, msg: AllMessages) -> Option { + fn intercept_outgoing( + &self, + msg: overseer::CandidateBackingOutgoingMessages, + ) -> Option { let msg = match msg { - AllMessages::CollatorProtocol(CollatorProtocolMessage::Seconded( - relay_parent, - statement, - )) => { + overseer::CandidateBackingOutgoingMessages::CollatorProtocolMessage( + CollatorProtocolMessage::Seconded(relay_parent, statement), + ) => { // `parachain::collator-protocol: received an unexpected `CollationSeconded`: unknown statement statement=...` // TODO: Fix this error. We get this on colaltors because `malicious backing` creates a candidate that gets backed/included. // It is harmless for test parachain collators, but it will prevent cumulus based collators to make progress // as they wait for the relay chain to confirm the seconding of the collation. - AllMessages::CollatorProtocol(CollatorProtocolMessage::Seconded( - relay_parent, - statement, - )) + overseer::CandidateBackingOutgoingMessages::CollatorProtocolMessage( + CollatorProtocolMessage::Seconded(relay_parent, statement), + ) }, msg => msg, }; diff --git a/node/metrics/src/lib.rs b/node/metrics/src/lib.rs index d648304ae751..0390788dde17 100644 --- a/node/metrics/src/lib.rs +++ b/node/metrics/src/lib.rs @@ -41,7 +41,7 @@ pub fn logger_hook() -> impl FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Con |_logger_builder, _config| {} } -/// This module reexports Prometheus types and defines the [`Metrics`] trait. +/// This module reexports Prometheus types and defines the [`Metrics`](metrics::Metrics) trait. pub mod metrics { /// Reexport Substrate Prometheus types. pub use substrate_prometheus_endpoint as prometheus; diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index c2bbd1b4583b..a4f19b741ad5 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -34,8 +34,7 @@ use polkadot_node_subsystem::{ ApprovalCheckResult, ApprovalDistributionMessage, ApprovalVotingMessage, AssignmentCheckResult, NetworkBridgeEvent, NetworkBridgeMessage, }, - overseer, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemContext, - SubsystemError, + overseer, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemError, }; use polkadot_primitives::v2::{ BlockNumber, CandidateIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature, @@ -321,11 +320,11 @@ enum PendingMessage { Approval(IndirectSignedApprovalVote), } +#[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)] impl State { - async fn handle_network_msg( + async fn handle_network_msg( &mut self, - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + ctx: &mut Context, metrics: &Metrics, event: NetworkBridgeEvent, rng: &mut (impl CryptoRng + Rng), @@ -377,10 +376,9 @@ impl State { } } - async fn handle_new_blocks( + async fn handle_new_blocks( &mut self, - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + ctx: &mut Context, metrics: &Metrics, metas: Vec, rng: &mut (impl CryptoRng + Rng), @@ -421,11 +419,12 @@ impl State { ); { + let sender = ctx.sender(); for (peer_id, view) in self.peer_views.iter() { let intersection = view.iter().filter(|h| new_hashes.contains(h)); let view_intersection = View::new(intersection.cloned(), view.finalized_number); Self::unify_with_peer( - ctx, + sender, metrics, &mut self.blocks, &self.topologies, @@ -496,10 +495,9 @@ impl State { self.enable_aggression(ctx, Resend::Yes, metrics).await; } - async fn handle_new_session_topology( + async fn handle_new_session_topology( &mut self, - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + ctx: &mut Context, session: SessionIndex, topology: SessionGridTopology, ) { @@ -520,15 +518,16 @@ impl State { .await; } - async fn process_incoming_peer_message( + async fn process_incoming_peer_message( &mut self, - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + ctx: &mut Context, metrics: &Metrics, peer_id: PeerId, msg: protocol_v1::ApprovalDistributionMessage, - rng: &mut (impl CryptoRng + Rng), - ) { + rng: &mut R, + ) where + R: CryptoRng + Rng, + { match msg { protocol_v1::ApprovalDistributionMessage::Assignments(assignments) => { gum::trace!( @@ -612,15 +611,16 @@ impl State { // handle a peer view change: requires that the peer is already connected // and has an entry in the `PeerData` struct. - async fn handle_peer_view_change( + async fn handle_peer_view_change( &mut self, - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + ctx: &mut Context, metrics: &Metrics, peer_id: PeerId, view: View, - rng: &mut (impl CryptoRng + Rng), - ) { + rng: &mut R, + ) where + R: CryptoRng + Rng, + { gum::trace!(target: LOG_TARGET, ?view, "Peer view change"); let finalized_number = view.finalized_number; let old_view = @@ -646,7 +646,7 @@ impl State { } Self::unify_with_peer( - ctx, + ctx.sender(), metrics, &mut self.blocks, &self.topologies, @@ -658,10 +658,9 @@ impl State { .await; } - async fn handle_block_finalized( + async fn handle_block_finalized( &mut self, - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + ctx: &mut Context, metrics: &Metrics, finalized_number: BlockNumber, ) { @@ -687,16 +686,17 @@ impl State { self.enable_aggression(ctx, Resend::No, metrics).await; } - async fn import_and_circulate_assignment( + async fn import_and_circulate_assignment( &mut self, - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + ctx: &mut Context, metrics: &Metrics, source: MessageSource, assignment: IndirectAssignmentCert, claimed_candidate_index: CandidateIndex, - rng: &mut (impl CryptoRng + Rng), - ) { + rng: &mut R, + ) where + R: CryptoRng + Rng, + { let block_hash = assignment.block_hash.clone(); let validator_index = assignment.validator; @@ -712,7 +712,7 @@ impl State { "Unexpected assignment", ); if !self.recent_outdated_blocks.is_recent_outdated(&block_hash) { - modify_reputation(ctx, peer_id, COST_UNEXPECTED_MESSAGE).await; + modify_reputation(ctx.sender(), peer_id, COST_UNEXPECTED_MESSAGE).await; } } return @@ -737,7 +737,7 @@ impl State { ?message_subject, "Duplicate assignment", ); - modify_reputation(ctx, peer_id, COST_DUPLICATE_MESSAGE).await; + modify_reputation(ctx.sender(), peer_id, COST_DUPLICATE_MESSAGE).await; } return } @@ -749,13 +749,13 @@ impl State { ?message_subject, "Assignment from a peer is out of view", ); - modify_reputation(ctx, peer_id.clone(), COST_UNEXPECTED_MESSAGE).await; + modify_reputation(ctx.sender(), peer_id.clone(), COST_UNEXPECTED_MESSAGE).await; }, } // if the assignment is known to be valid, reward the peer if entry.knowledge.contains(&message_subject, message_kind) { - modify_reputation(ctx, peer_id.clone(), BENEFIT_VALID_MESSAGE).await; + modify_reputation(ctx.sender(), peer_id.clone(), BENEFIT_VALID_MESSAGE).await; if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { gum::trace!(target: LOG_TARGET, ?peer_id, ?message_subject, "Known assignment"); peer_knowledge.received.insert(message_subject, message_kind); @@ -791,7 +791,8 @@ impl State { ); match result { AssignmentCheckResult::Accepted => { - modify_reputation(ctx, peer_id.clone(), BENEFIT_VALID_MESSAGE_FIRST).await; + modify_reputation(ctx.sender(), peer_id.clone(), BENEFIT_VALID_MESSAGE_FIRST) + .await; entry.knowledge.known_messages.insert(message_subject.clone(), message_kind); if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { peer_knowledge.received.insert(message_subject.clone(), message_kind); @@ -819,7 +820,8 @@ impl State { ?peer_id, "Got an assignment too far in the future", ); - modify_reputation(ctx, peer_id, COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE).await; + modify_reputation(ctx.sender(), peer_id, COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE) + .await; return }, AssignmentCheckResult::Bad(error) => { @@ -830,7 +832,7 @@ impl State { %error, "Got a bad assignment from peer", ); - modify_reputation(ctx, peer_id, COST_INVALID_MESSAGE).await; + modify_reputation(ctx.sender(), peer_id, COST_INVALID_MESSAGE).await; return }, } @@ -946,10 +948,9 @@ impl State { } } - async fn import_and_circulate_approval( + async fn import_and_circulate_approval( &mut self, - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + ctx: &mut Context, metrics: &Metrics, source: MessageSource, vote: IndirectSignedApprovalVote, @@ -963,7 +964,7 @@ impl State { _ => { if let Some(peer_id) = source.peer_id() { if !self.recent_outdated_blocks.is_recent_outdated(&block_hash) { - modify_reputation(ctx, peer_id, COST_UNEXPECTED_MESSAGE).await; + modify_reputation(ctx.sender(), peer_id, COST_UNEXPECTED_MESSAGE).await; } } return @@ -982,7 +983,7 @@ impl State { ?message_subject, "Unknown approval assignment", ); - modify_reputation(ctx, peer_id, COST_UNEXPECTED_MESSAGE).await; + modify_reputation(ctx.sender(), peer_id, COST_UNEXPECTED_MESSAGE).await; return } @@ -999,7 +1000,7 @@ impl State { "Duplicate approval", ); - modify_reputation(ctx, peer_id, COST_DUPLICATE_MESSAGE).await; + modify_reputation(ctx.sender(), peer_id, COST_DUPLICATE_MESSAGE).await; } return } @@ -1011,14 +1012,14 @@ impl State { ?message_subject, "Approval from a peer is out of view", ); - modify_reputation(ctx, peer_id.clone(), COST_UNEXPECTED_MESSAGE).await; + modify_reputation(ctx.sender(), peer_id.clone(), COST_UNEXPECTED_MESSAGE).await; }, } // if the approval is known to be valid, reward the peer if entry.knowledge.contains(&message_subject, message_kind) { gum::trace!(target: LOG_TARGET, ?peer_id, ?message_subject, "Known approval"); - modify_reputation(ctx, peer_id.clone(), BENEFIT_VALID_MESSAGE).await; + modify_reputation(ctx.sender(), peer_id.clone(), BENEFIT_VALID_MESSAGE).await; if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { peer_knowledge.received.insert(message_subject.clone(), message_kind); } @@ -1049,7 +1050,8 @@ impl State { ); match result { ApprovalCheckResult::Accepted => { - modify_reputation(ctx, peer_id.clone(), BENEFIT_VALID_MESSAGE_FIRST).await; + modify_reputation(ctx.sender(), peer_id.clone(), BENEFIT_VALID_MESSAGE_FIRST) + .await; entry.knowledge.insert(message_subject.clone(), message_kind); if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { @@ -1057,7 +1059,7 @@ impl State { } }, ApprovalCheckResult::Bad(error) => { - modify_reputation(ctx, peer_id, COST_INVALID_MESSAGE).await; + modify_reputation(ctx.sender(), peer_id, COST_INVALID_MESSAGE).await; gum::info!( target: LOG_TARGET, ?peer_id, @@ -1209,8 +1211,7 @@ impl State { } async fn unify_with_peer( - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + sender: &mut impl overseer::ApprovalDistributionSenderTrait, metrics: &Metrics, entries: &mut HashMap, topologies: &SessionGridTopologies, @@ -1326,13 +1327,14 @@ impl State { "Sending assignments to unified peer", ); - ctx.send_message(NetworkBridgeMessage::SendValidationMessage( - vec![peer_id.clone()], - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments_to_send), - )), - )) - .await; + sender + .send_message(NetworkBridgeMessage::SendValidationMessage( + vec![peer_id.clone()], + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments_to_send), + )), + )) + .await; } if !approvals_to_send.is_empty() { @@ -1343,20 +1345,20 @@ impl State { "Sending approvals to unified peer", ); - ctx.send_message(NetworkBridgeMessage::SendValidationMessage( - vec![peer_id], - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(approvals_to_send), - )), - )) - .await; + sender + .send_message(NetworkBridgeMessage::SendValidationMessage( + vec![peer_id], + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(approvals_to_send), + )), + )) + .await; } } - async fn enable_aggression( + async fn enable_aggression( &mut self, - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + ctx: &mut Context, resend: Resend, metrics: &Metrics, ) { @@ -1457,14 +1459,17 @@ impl State { // // Note that the required routing of a message can be modified even if the // topology is unknown yet. -async fn adjust_required_routing_and_propagate( - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), +#[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)] +async fn adjust_required_routing_and_propagate( + ctx: &mut Context, blocks: &mut HashMap, topologies: &SessionGridTopologies, - block_filter: impl Fn(&mut BlockEntry) -> bool, - routing_modifier: impl Fn(&mut RequiredRouting, bool, &ValidatorIndex), -) { + block_filter: BlockFilter, + routing_modifier: RoutingModifier, +) where + BlockFilter: Fn(&mut BlockEntry) -> bool, + RoutingModifier: Fn(&mut RequiredRouting, bool, &ValidatorIndex), +{ let mut peer_assignments = HashMap::new(); let mut peer_approvals = HashMap::new(); @@ -1566,8 +1571,7 @@ async fn adjust_required_routing_and_propagate( /// Modify the reputation of a peer based on its behavior. async fn modify_reputation( - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + sender: &mut impl overseer::ApprovalDistributionSenderTrait, peer_id: PeerId, rep: Rep, ) { @@ -1578,20 +1582,17 @@ async fn modify_reputation( "Reputation change for peer", ); - ctx.send_message(NetworkBridgeMessage::ReportPeer(peer_id, rep)).await; + sender.send_message(NetworkBridgeMessage::ReportPeer(peer_id, rep)).await; } +#[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)] impl ApprovalDistribution { /// Create a new instance of the [`ApprovalDistribution`] subsystem. pub fn new(metrics: Metrics) -> Self { Self { metrics } } - async fn run(self, ctx: Context) - where - Context: SubsystemContext, - Context: overseer::SubsystemContext, - { + async fn run(self, ctx: Context) { let mut state = State::default(); // According to the docs of `rand`, this is a ChaCha12 RNG in practice @@ -1606,10 +1607,7 @@ impl ApprovalDistribution { mut ctx: Context, state: &mut State, rng: &mut (impl CryptoRng + Rng), - ) where - Context: SubsystemContext, - Context: overseer::SubsystemContext, - { + ) { loop { let message = match ctx.recv().await { Ok(message) => message, @@ -1644,10 +1642,7 @@ impl ApprovalDistribution { msg: ApprovalDistributionMessage, metrics: &Metrics, rng: &mut (impl CryptoRng + Rng), - ) where - Context: SubsystemContext, - Context: overseer::SubsystemContext, - { + ) { match msg { ApprovalDistributionMessage::NetworkBridgeUpdate(event) => { state.handle_network_msg(ctx, metrics, event, rng).await; @@ -1690,11 +1685,8 @@ impl ApprovalDistribution { } } -impl overseer::Subsystem for ApprovalDistribution -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::subsystem(ApprovalDistribution, error=SubsystemError, prefix=self::overseer)] +impl ApprovalDistribution { fn start(self, ctx: Context) -> SpawnedSubsystem { let future = self.run(ctx).map(|_| Ok(())).boxed(); diff --git a/node/network/availability-distribution/src/lib.rs b/node/network/availability-distribution/src/lib.rs index f26a2c78e5d7..392b3a8dfd3b 100644 --- a/node/network/availability-distribution/src/lib.rs +++ b/node/network/availability-distribution/src/lib.rs @@ -21,7 +21,7 @@ use sp_keystore::SyncCryptoStorePtr; use polkadot_node_network_protocol::request_response::{v1, IncomingRequestReceiver}; use polkadot_node_subsystem::{ messages::AvailabilityDistributionMessage, overseer, FromOverseer, OverseerSignal, - SpawnedSubsystem, SubsystemContext, SubsystemError, + SpawnedSubsystem, SubsystemError, }; /// Error and [`Result`] type for this subsystem. @@ -68,11 +68,8 @@ pub struct IncomingRequestReceivers { pub chunk_req_receiver: IncomingRequestReceiver, } -impl overseer::Subsystem for AvailabilityDistributionSubsystem -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::subsystem(AvailabilityDistribution, error=SubsystemError, prefix=self::overseer)] +impl AvailabilityDistributionSubsystem { fn start(self, ctx: Context) -> SpawnedSubsystem { let future = self .run(ctx) @@ -83,6 +80,7 @@ where } } +#[overseer::contextbounds(AvailabilityDistribution, prefix = self::overseer)] impl AvailabilityDistributionSubsystem { /// Create a new instance of the availability distribution. pub fn new( @@ -95,11 +93,7 @@ impl AvailabilityDistributionSubsystem { } /// Start processing work as passed on from the Overseer. - async fn run(self, mut ctx: Context) -> std::result::Result<(), FatalError> - where - Context: SubsystemContext, - Context: overseer::SubsystemContext, - { + async fn run(self, mut ctx: Context) -> std::result::Result<(), FatalError> { let Self { mut runtime, recvs, metrics } = self; let IncomingRequestReceivers { pov_req_receiver, chunk_req_receiver } = recvs; diff --git a/node/network/availability-distribution/src/pov_requester/mod.rs b/node/network/availability-distribution/src/pov_requester/mod.rs index b1b9af35ef4e..4d866894e8f6 100644 --- a/node/network/availability-distribution/src/pov_requester/mod.rs +++ b/node/network/availability-distribution/src/pov_requester/mod.rs @@ -27,7 +27,7 @@ use polkadot_node_primitives::PoV; use polkadot_node_subsystem::{ jaeger, messages::{IfDisconnected, NetworkBridgeMessage}, - SubsystemContext, + overseer, }; use polkadot_node_subsystem_util::runtime::RuntimeInfo; use polkadot_primitives::v2::{AuthorityDiscoveryId, CandidateHash, Hash, ValidatorIndex}; @@ -39,6 +39,7 @@ use crate::{ }; /// Start background worker for taking care of fetching the requested `PoV` from the network. +#[overseer::contextbounds(AvailabilityDistribution, prefix = self::overseer)] pub async fn fetch_pov( ctx: &mut Context, runtime: &mut RuntimeInfo, @@ -48,10 +49,7 @@ pub async fn fetch_pov( pov_hash: Hash, tx: oneshot::Sender, metrics: Metrics, -) -> Result<()> -where - Context: SubsystemContext, -{ +) -> Result<()> { let info = &runtime.get_session_info(ctx.sender(), parent).await?.session_info; let authority_id = info .discovery_keys diff --git a/node/network/availability-distribution/src/requester/fetch_task/mod.rs b/node/network/availability-distribution/src/requester/fetch_task/mod.rs index 9945dd1b4a53..ff1e5e6a73f3 100644 --- a/node/network/availability-distribution/src/requester/fetch_task/mod.rs +++ b/node/network/availability-distribution/src/requester/fetch_task/mod.rs @@ -30,8 +30,8 @@ use polkadot_node_network_protocol::request_response::{ use polkadot_node_primitives::ErasureChunk; use polkadot_node_subsystem::{ jaeger, - messages::{AllMessages, AvailabilityStoreMessage, IfDisconnected, NetworkBridgeMessage}, - SubsystemContext, + messages::{AvailabilityStoreMessage, IfDisconnected, NetworkBridgeMessage}, + overseer, }; use polkadot_primitives::v2::{ AuthorityDiscoveryId, BlakeTwo256, CandidateHash, GroupIndex, Hash, HashT, OccupiedCore, @@ -84,7 +84,7 @@ enum FetchedState { /// Messages sent from `FetchTask`s to be handled/forwarded. pub enum FromFetchTask { /// Message to other subsystem. - Message(AllMessages), + Message(overseer::AvailabilityDistributionOutgoingMessages), /// Concluded with result. /// @@ -171,14 +171,12 @@ impl FetchTaskConfig { } } +#[overseer::contextbounds(AvailabilityDistribution, prefix = self::overseer)] impl FetchTask { /// Start fetching a chunk. /// /// A task handling the fetching of the configured chunk will be spawned. - pub async fn start(config: FetchTaskConfig, ctx: &mut Context) -> Result - where - Context: SubsystemContext, - { + pub async fn start(config: FetchTaskConfig, ctx: &mut Context) -> Result { let FetchTaskConfig { prepared_running, live_in } = config; if let Some(running) = prepared_running { @@ -333,9 +331,10 @@ impl RunningTask { let requests = Requests::ChunkFetchingV1(full_request); self.sender - .send(FromFetchTask::Message(AllMessages::NetworkBridge( - NetworkBridgeMessage::SendRequests(vec![requests], IfDisconnected::ImmediateError), - ))) + .send(FromFetchTask::Message( + NetworkBridgeMessage::SendRequests(vec![requests], IfDisconnected::ImmediateError) + .into(), + )) .await .map_err(|_| TaskError::ShuttingDown)?; @@ -413,13 +412,14 @@ impl RunningTask { let (tx, rx) = oneshot::channel(); let r = self .sender - .send(FromFetchTask::Message(AllMessages::AvailabilityStore( + .send(FromFetchTask::Message( AvailabilityStoreMessage::StoreChunk { candidate_hash: self.request.candidate_hash, chunk, tx, - }, - ))) + } + .into(), + )) .await; if let Err(err) = r { gum::error!(target: LOG_TARGET, err= ?err, "Storing erasure chunk failed, system shutting down?"); diff --git a/node/network/availability-distribution/src/requester/fetch_task/tests.rs b/node/network/availability-distribution/src/requester/fetch_task/tests.rs index dea0d187f2d8..3030cb877ec7 100644 --- a/node/network/availability-distribution/src/requester/fetch_task/tests.rs +++ b/node/network/availability-distribution/src/requester/fetch_task/tests.rs @@ -227,7 +227,11 @@ impl TestRun { /// Returns true, if after processing of the given message it would be OK for the stream to /// end. - async fn handle_message(&self, msg: AllMessages) -> bool { + async fn handle_message( + &self, + msg: overseer::AvailabilityDistributionOutgoingMessages, + ) -> bool { + let msg = AllMessages::from(msg); match msg { AllMessages::NetworkBridge(NetworkBridgeMessage::SendRequests( reqs, diff --git a/node/network/availability-distribution/src/requester/mod.rs b/node/network/availability-distribution/src/requester/mod.rs index b1ea7a98c1f3..105efd2f8948 100644 --- a/node/network/availability-distribution/src/requester/mod.rs +++ b/node/network/availability-distribution/src/requester/mod.rs @@ -33,8 +33,8 @@ use futures::{ }; use polkadot_node_subsystem::{ - messages::{AllMessages, ChainApiMessage}, - ActivatedLeaf, ActiveLeavesUpdate, LeafStatus, SubsystemContext, + messages::{ChainApiMessage, RuntimeApiMessage}, + overseer, ActivatedLeaf, ActiveLeavesUpdate, LeafStatus, }; use polkadot_node_subsystem_util::runtime::{get_occupied_cores, RuntimeInfo}; use polkadot_primitives::v2::{CandidateHash, Hash, OccupiedCore, SessionIndex}; @@ -78,6 +78,7 @@ pub struct Requester { metrics: Metrics, } +#[overseer::contextbounds(AvailabilityDistribution, prefix = self::overseer)] impl Requester { /// How many ancestors of the leaf should we consider along with it. pub(crate) const LEAF_ANCESTRY_LEN_WITHIN_SESSION: usize = 3; @@ -99,10 +100,7 @@ impl Requester { ctx: &mut Context, runtime: &mut RuntimeInfo, update: ActiveLeavesUpdate, - ) -> Result<()> - where - Context: SubsystemContext, - { + ) -> Result<()> { gum::trace!(target: LOG_TARGET, ?update, "Update fetching heads"); let ActiveLeavesUpdate { activated, deactivated } = update; // Stale leaves happen after a reversion - we don't want to re-run availability there. @@ -125,13 +123,11 @@ impl Requester { ctx: &mut Context, runtime: &mut RuntimeInfo, new_head: ActivatedLeaf, - ) -> Result<()> - where - Context: SubsystemContext, - { + ) -> Result<()> { + let sender = &mut ctx.sender().clone(); let ActivatedLeaf { hash: leaf, .. } = new_head; let (leaf_session_index, ancestors_in_session) = get_block_ancestors_in_same_session( - ctx, + sender, runtime, leaf, Self::LEAF_ANCESTRY_LEN_WITHIN_SESSION, @@ -139,7 +135,7 @@ impl Requester { .await?; // Also spawn or bump tasks for candidates in ancestry in the same session. for hash in std::iter::once(leaf).chain(ancestors_in_session) { - let cores = get_occupied_cores(ctx, hash).await?; + let cores = get_occupied_cores(sender, hash).await?; gum::trace!( target: LOG_TARGET, occupied_cores = ?cores, @@ -177,15 +173,12 @@ impl Requester { /// passed in leaf might be some later block where the candidate is still pending availability. async fn add_cores( &mut self, - ctx: &mut Context, + context: &mut Context, runtime: &mut RuntimeInfo, leaf: Hash, leaf_session_index: SessionIndex, cores: impl IntoIterator, - ) -> Result<()> - where - Context: SubsystemContext, - { + ) -> Result<()> { for core in cores { match self.fetches.entry(core.candidate_hash) { Entry::Occupied(mut e) => @@ -200,7 +193,7 @@ impl Requester { let task_cfg = self .session_cache .with_session_info( - ctx, + context, runtime, // We use leaf here, the relay_parent must be in the same session as the // leaf. This is guaranteed by runtime which ensures that cores are cleared @@ -221,7 +214,7 @@ impl Requester { }); if let Ok(Some(task_cfg)) = task_cfg { - e.insert(FetchTask::start(task_cfg, ctx).await?); + e.insert(FetchTask::start(task_cfg, context).await?); } // Not a validator, nothing to do. }, @@ -232,9 +225,9 @@ impl Requester { } impl Stream for Requester { - type Item = AllMessages; + type Item = overseer::AvailabilityDistributionOutgoingMessages; - fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { loop { match Pin::new(&mut self.rx).poll_next(ctx) { Poll::Ready(Some(FromFetchTask::Message(m))) => return Poll::Ready(Some(m)), @@ -257,26 +250,27 @@ impl Stream for Requester { /// Requests up to `limit` ancestor hashes of relay parent in the same session. /// /// Also returns session index of the `head`. -async fn get_block_ancestors_in_same_session( - ctx: &mut Context, +async fn get_block_ancestors_in_same_session( + sender: &mut Sender, runtime: &mut RuntimeInfo, head: Hash, limit: usize, ) -> Result<(SessionIndex, Vec)> where - Context: SubsystemContext, + Sender: + overseer::SubsystemSender + overseer::SubsystemSender, { // The order is parent, grandparent, ... // // `limit + 1` since a session index for the last element in ancestry // is obtained through its parent. It always gets truncated because // `session_ancestry_len` can only be incremented `ancestors.len() - 1` times. - let mut ancestors = get_block_ancestors(ctx, head, limit + 1).await?; + let mut ancestors = get_block_ancestors(sender, head, limit + 1).await?; let mut ancestors_iter = ancestors.iter(); // `head` is the child of the first block in `ancestors`, request its session index. let head_session_index = match ancestors_iter.next() { - Some(parent) => runtime.get_session_index_for_child(ctx.sender(), *parent).await?, + Some(parent) => runtime.get_session_index_for_child(sender, *parent).await?, None => { // No first element, i.e. empty. return Ok((0, ancestors)) @@ -287,7 +281,7 @@ where // The first parent is skipped. for parent in ancestors_iter { // Parent is the i-th ancestor, request session index for its child -- (i-1)th element. - let session_index = runtime.get_session_index_for_child(ctx.sender(), *parent).await?; + let session_index = runtime.get_session_index_for_child(sender, *parent).await?; if session_index == head_session_index { session_ancestry_len += 1; } else { @@ -302,21 +296,22 @@ where } /// Request up to `limit` ancestor hashes of relay parent from the Chain API. -async fn get_block_ancestors( - ctx: &mut Context, +async fn get_block_ancestors( + sender: &mut Sender, relay_parent: Hash, limit: usize, ) -> Result> where - Context: SubsystemContext, + Sender: overseer::SubsystemSender, { let (tx, rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::Ancestors { - hash: relay_parent, - k: limit, - response_channel: tx, - }) - .await; + sender + .send_message(ChainApiMessage::Ancestors { + hash: relay_parent, + k: limit, + response_channel: tx, + }) + .await; let ancestors = rx .await diff --git a/node/network/availability-distribution/src/requester/session_cache.rs b/node/network/availability-distribution/src/requester/session_cache.rs index e9febc864322..6d41d9301233 100644 --- a/node/network/availability-distribution/src/requester/session_cache.rs +++ b/node/network/availability-distribution/src/requester/session_cache.rs @@ -19,7 +19,7 @@ use std::collections::HashSet; use lru::LruCache; use rand::{seq::SliceRandom, thread_rng}; -use polkadot_node_subsystem::SubsystemContext; +use polkadot_node_subsystem::overseer; use polkadot_node_subsystem_util::runtime::RuntimeInfo; use polkadot_primitives::v2::{ AuthorityDiscoveryId, GroupIndex, Hash, SessionIndex, ValidatorIndex, @@ -79,6 +79,7 @@ pub struct BadValidators { pub bad_validators: Vec, } +#[overseer::contextbounds(AvailabilityDistribution, prefix = self::overseer)] impl SessionCache { /// Create a new `SessionCache`. pub fn new() -> Self { @@ -103,7 +104,6 @@ impl SessionCache { with_info: F, ) -> Result> where - Context: SubsystemContext, F: FnOnce(&SessionInfo) -> R, { if let Some(o_info) = self.session_info_cache.get(&session_index) { @@ -178,10 +178,7 @@ impl SessionCache { runtime: &mut RuntimeInfo, relay_parent: Hash, session_index: SessionIndex, - ) -> Result> - where - Context: SubsystemContext, - { + ) -> Result> { let info = runtime .get_session_info_by_index(ctx.sender(), relay_parent, session_index) .await?; diff --git a/node/network/availability-distribution/src/responder.rs b/node/network/availability-distribution/src/responder.rs index 0520a86ca1d2..53c363823f72 100644 --- a/node/network/availability-distribution/src/responder.rs +++ b/node/network/availability-distribution/src/responder.rs @@ -43,7 +43,7 @@ pub async fn run_pov_receiver( mut receiver: IncomingRequestReceiver, metrics: Metrics, ) where - Sender: SubsystemSender, + Sender: SubsystemSender, { loop { match receiver.recv(|| vec![COST_INVALID_REQUEST]).await.into_nested() { @@ -71,7 +71,7 @@ pub async fn run_chunk_receiver( mut receiver: IncomingRequestReceiver, metrics: Metrics, ) where - Sender: SubsystemSender, + Sender: SubsystemSender, { loop { match receiver.recv(|| vec![COST_INVALID_REQUEST]).await.into_nested() { @@ -105,7 +105,7 @@ pub async fn answer_pov_request_log( req: IncomingRequest, metrics: &Metrics, ) where - Sender: SubsystemSender, + Sender: SubsystemSender, { let res = answer_pov_request(sender, req).await; match res { @@ -130,7 +130,7 @@ pub async fn answer_chunk_request_log( metrics: &Metrics, ) -> () where - Sender: SubsystemSender, + Sender: SubsystemSender, { let res = answer_chunk_request(sender, req).await; match res { @@ -154,7 +154,7 @@ pub async fn answer_pov_request( req: IncomingRequest, ) -> Result where - Sender: SubsystemSender, + Sender: SubsystemSender, { let _span = jaeger::Span::new(req.payload.candidate_hash, "answer-pov-request"); @@ -182,7 +182,7 @@ pub async fn answer_chunk_request( req: IncomingRequest, ) -> Result where - Sender: SubsystemSender, + Sender: SubsystemSender, { let span = jaeger::Span::new(req.payload.candidate_hash, "answer-chunk-request"); @@ -217,7 +217,7 @@ async fn query_chunk( validator_index: ValidatorIndex, ) -> std::result::Result, JfyiError> where - Sender: SubsystemSender, + Sender: SubsystemSender, { let (tx, rx) = oneshot::channel(); sender @@ -245,7 +245,7 @@ async fn query_available_data( candidate_hash: CandidateHash, ) -> Result> where - Sender: SubsystemSender, + Sender: SubsystemSender, { let (tx, rx) = oneshot::channel(); sender diff --git a/node/network/availability-recovery/src/lib.rs b/node/network/availability-recovery/src/lib.rs index 533d4ec94f8d..c23397975b55 100644 --- a/node/network/availability-recovery/src/lib.rs +++ b/node/network/availability-recovery/src/lib.rs @@ -51,9 +51,8 @@ use polkadot_node_subsystem::{ errors::RecoveryError, jaeger, messages::{AvailabilityRecoveryMessage, AvailabilityStoreMessage, NetworkBridgeMessage}, - overseer::{self, Subsystem}, - ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemContext, - SubsystemError, SubsystemResult, SubsystemSender, + overseer, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemError, + SubsystemResult, }; use polkadot_node_subsystem_util::request_session_info; use polkadot_primitives::v2::{ @@ -156,8 +155,8 @@ enum Source { /// A stateful reconstruction of availability data in reference to /// a candidate hash. -struct RecoveryTask { - sender: S, +struct RecoveryTask { + sender: Sender, /// The parameters of the recovery process. params: RecoveryParams, @@ -177,7 +176,7 @@ impl RequestFromBackers { async fn run( &mut self, params: &RecoveryParams, - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::AvailabilityRecoverySenderTrait, ) -> Result { gum::trace!( target: LOG_TARGET, @@ -199,13 +198,10 @@ impl RequestFromBackers { ); sender - .send_message( - NetworkBridgeMessage::SendRequests( - vec![Requests::AvailableDataFetchingV1(req)], - IfDisconnected::ImmediateError, - ) - .into(), - ) + .send_message(NetworkBridgeMessage::SendRequests( + vec![Requests::AvailableDataFetchingV1(req)], + IfDisconnected::ImmediateError, + )) .await; match response.await { @@ -298,11 +294,13 @@ impl RequestChunksFromValidators { ) } - async fn launch_parallel_requests( + async fn launch_parallel_requests( &mut self, params: &RecoveryParams, - sender: &mut impl SubsystemSender, - ) { + sender: &mut Sender, + ) where + Sender: overseer::AvailabilityRecoverySenderTrait, + { let num_requests = self.get_desired_request_count(params.threshold); let candidate_hash = ¶ms.candidate_hash; let already_requesting_count = self.requesting_chunks.len(); @@ -358,9 +356,10 @@ impl RequestChunksFromValidators { } sender - .send_message( - NetworkBridgeMessage::SendRequests(requests, IfDisconnected::ImmediateError).into(), - ) + .send_message(NetworkBridgeMessage::SendRequests( + requests, + IfDisconnected::ImmediateError, + )) .await; } @@ -483,20 +482,21 @@ impl RequestChunksFromValidators { } } - async fn run( + async fn run( &mut self, params: &RecoveryParams, - sender: &mut impl SubsystemSender, - ) -> Result { + sender: &mut Sender, + ) -> Result + where + Sender: overseer::AvailabilityRecoverySenderTrait, + { let metrics = ¶ms.metrics; // First query the store for any chunks we've got. { let (tx, rx) = oneshot::channel(); sender - .send_message( - AvailabilityStoreMessage::QueryAllChunks(params.candidate_hash, tx).into(), - ) + .send_message(AvailabilityStoreMessage::QueryAllChunks(params.candidate_hash, tx)) .await; match rx.await { @@ -646,16 +646,19 @@ fn reconstructed_data_matches_root( branches.root() == *expected_root } -impl RecoveryTask { +impl RecoveryTask +where + Sender: overseer::AvailabilityRecoverySenderTrait, +{ async fn run(mut self) -> Result { // First just see if we have the data available locally. { let (tx, rx) = oneshot::channel(); self.sender - .send_message( - AvailabilityStoreMessage::QueryAvailableData(self.params.candidate_hash, tx) - .into(), - ) + .send_message(AvailabilityStoreMessage::QueryAvailableData( + self.params.candidate_hash, + tx, + )) .await; match rx.await { @@ -799,11 +802,8 @@ impl Default for State { } } -impl Subsystem for AvailabilityRecoverySubsystem -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::subsystem(AvailabilityRecovery, error=SubsystemError, prefix=self::overseer)] +impl AvailabilityRecoverySubsystem { fn start(self, ctx: Context) -> SpawnedSubsystem { let future = self .run(ctx) @@ -832,6 +832,7 @@ async fn handle_signal(state: &mut State, signal: OverseerSignal) -> SubsystemRe } /// Machinery around launching recovery tasks into the background. +#[overseer::contextbounds(AvailabilityRecovery, prefix = self::overseer)] async fn launch_recovery_task( state: &mut State, ctx: &mut Context, @@ -840,11 +841,7 @@ async fn launch_recovery_task( backing_group: Option, response_sender: oneshot::Sender>, metrics: &Metrics, -) -> error::Result<()> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> error::Result<()> { let candidate_hash = receipt.hash(); let params = RecoveryParams { @@ -885,6 +882,7 @@ where } /// Handles an availability recovery request. +#[overseer::contextbounds(AvailabilityRecovery, prefix = self::overseer)] async fn handle_recover( state: &mut State, ctx: &mut Context, @@ -893,11 +891,7 @@ async fn handle_recover( backing_group: Option, response_sender: oneshot::Sender>, metrics: &Metrics, -) -> error::Result<()> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> error::Result<()> { let candidate_hash = receipt.hash(); let span = jaeger::Span::new(candidate_hash, "availbility-recovery") @@ -953,14 +947,11 @@ where } /// Queries a chunk from av-store. +#[overseer::contextbounds(AvailabilityRecovery, prefix = self::overseer)] async fn query_full_data( ctx: &mut Context, candidate_hash: CandidateHash, -) -> error::Result> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> error::Result> { let (tx, rx) = oneshot::channel(); ctx.send_message(AvailabilityStoreMessage::QueryAvailableData(candidate_hash, tx)) .await; @@ -968,6 +959,7 @@ where Ok(rx.await.map_err(error::Error::CanceledQueryFullData)?) } +#[overseer::contextbounds(AvailabilityRecovery, prefix = self::overseer)] impl AvailabilityRecoverySubsystem { /// Create a new instance of `AvailabilityRecoverySubsystem` which starts with a fast path to /// request data from backers. @@ -986,11 +978,7 @@ impl AvailabilityRecoverySubsystem { Self { fast_path: false, req_receiver, metrics } } - async fn run(self, mut ctx: Context) -> SubsystemResult<()> - where - Context: SubsystemContext, - Context: overseer::SubsystemContext, - { + async fn run(self, mut ctx: Context) -> SubsystemResult<()> { let mut state = State::default(); let Self { fast_path, mut req_receiver, metrics } = self; diff --git a/node/network/bitfield-distribution/src/lib.rs b/node/network/bitfield-distribution/src/lib.rs index c2a17c5a42cb..e3ed0e6f89a9 100644 --- a/node/network/bitfield-distribution/src/lib.rs +++ b/node/network/bitfield-distribution/src/lib.rs @@ -31,7 +31,7 @@ use polkadot_node_network_protocol::{ }; use polkadot_node_subsystem::{ jaeger, messages::*, overseer, ActiveLeavesUpdate, FromOverseer, OverseerSignal, PerLeafSpan, - SpawnedSubsystem, SubsystemContext, SubsystemError, SubsystemResult, + SpawnedSubsystem, SubsystemError, SubsystemResult, }; use polkadot_node_subsystem_util::{self as util}; use polkadot_primitives::v2::{ @@ -204,6 +204,7 @@ pub struct BitfieldDistribution { metrics: Metrics, } +#[overseer::contextbounds(BitfieldDistribution, prefix = self::overseer)] impl BitfieldDistribution { /// Create a new instance of the `BitfieldDistribution` subsystem. pub fn new(metrics: Metrics) -> Self { @@ -211,11 +212,7 @@ impl BitfieldDistribution { } /// Start processing work as passed on from the Overseer. - async fn run(self, ctx: Context) - where - Context: SubsystemContext, - Context: overseer::SubsystemContext, - { + async fn run(self, ctx: Context) { let mut state = ProtocolState::default(); let mut rng = rand::rngs::StdRng::from_entropy(); self.run_inner(ctx, &mut state, &mut rng).await @@ -226,10 +223,7 @@ impl BitfieldDistribution { mut ctx: Context, state: &mut ProtocolState, rng: &mut (impl CryptoRng + Rng), - ) where - Context: SubsystemContext, - Context: overseer::SubsystemContext, - { + ) { // work: process incoming messages from the overseer and process accordingly. loop { @@ -316,17 +310,20 @@ impl BitfieldDistribution { } /// Modify the reputation of a peer based on its behavior. -async fn modify_reputation(ctx: &mut Context, relay_parent: Hash, peer: PeerId, rep: Rep) -where - Context: SubsystemContext, -{ +async fn modify_reputation( + sender: &mut impl overseer::BitfieldDistributionSenderTrait, + relay_parent: Hash, + peer: PeerId, + rep: Rep, +) { gum::trace!(target: LOG_TARGET, ?relay_parent, ?rep, %peer, "reputation change"); - ctx.send_message(NetworkBridgeMessage::ReportPeer(peer, rep)).await + sender.send_message(NetworkBridgeMessage::ReportPeer(peer, rep)).await } /// Distribute a given valid and signature checked bitfield message. /// /// For this variant the source is this node. +#[overseer::contextbounds(BitfieldDistribution, prefix=self::overseer)] async fn handle_bitfield_distribution( ctx: &mut Context, state: &mut ProtocolState, @@ -334,9 +331,7 @@ async fn handle_bitfield_distribution( relay_parent: Hash, signed_availability: SignedAvailabilityBitfield, rng: &mut (impl CryptoRng + Rng), -) where - Context: SubsystemContext, -{ +) { let _timer = metrics.time_handle_bitfield_distribution(); // Ignore anything the overseer did not tell this subsystem to work on @@ -389,6 +384,7 @@ async fn handle_bitfield_distribution( /// Distribute a given valid and signature checked bitfield message. /// /// Can be originated by another subsystem or received via network from another peer. +#[overseer::contextbounds(BitfieldDistribution, prefix=self::overseer)] async fn relay_message( ctx: &mut Context, job_data: &mut PerRelayParentData, @@ -398,9 +394,7 @@ async fn relay_message( message: BitfieldGossipMessage, required_routing: RequiredRouting, rng: &mut (impl CryptoRng + Rng), -) where - Context: SubsystemContext, -{ +) { let relay_parent = message.relay_parent; let span = job_data.span.child("relay-msg"); @@ -478,6 +472,7 @@ async fn relay_message( } /// Handle an incoming message from a peer. +#[overseer::contextbounds(BitfieldDistribution, prefix=self::overseer)] async fn process_incoming_peer_message( ctx: &mut Context, state: &mut ProtocolState, @@ -485,9 +480,7 @@ async fn process_incoming_peer_message( origin: PeerId, message: protocol_v1::BitfieldDistributionMessage, rng: &mut (impl CryptoRng + Rng), -) where - Context: SubsystemContext, -{ +) { let protocol_v1::BitfieldDistributionMessage::Bitfield(relay_parent, bitfield) = message; gum::trace!( target: LOG_TARGET, @@ -497,7 +490,7 @@ async fn process_incoming_peer_message( ); // we don't care about this, not part of our view. if !state.view.contains(&relay_parent) { - modify_reputation(ctx, relay_parent, origin, COST_NOT_IN_VIEW).await; + modify_reputation(ctx.sender(), relay_parent, origin, COST_NOT_IN_VIEW).await; return } @@ -506,7 +499,7 @@ async fn process_incoming_peer_message( let job_data: &mut _ = if let Some(ref mut job_data) = job_data { job_data } else { - modify_reputation(ctx, relay_parent, origin, COST_NOT_IN_VIEW).await; + modify_reputation(ctx.sender(), relay_parent, origin, COST_NOT_IN_VIEW).await; return }; @@ -523,7 +516,7 @@ async fn process_incoming_peer_message( let validator_set = &job_data.validator_set; if validator_set.is_empty() { gum::trace!(target: LOG_TARGET, ?relay_parent, ?origin, "Validator set is empty",); - modify_reputation(ctx, relay_parent, origin, COST_MISSING_PEER_SESSION_KEY).await; + modify_reputation(ctx.sender(), relay_parent, origin, COST_MISSING_PEER_SESSION_KEY).await; return } @@ -533,7 +526,7 @@ async fn process_incoming_peer_message( let validator = if let Some(validator) = validator_set.get(validator_index.0 as usize) { validator.clone() } else { - modify_reputation(ctx, relay_parent, origin, COST_VALIDATOR_INDEX_INVALID).await; + modify_reputation(ctx.sender(), relay_parent, origin, COST_VALIDATOR_INDEX_INVALID).await; return }; @@ -546,7 +539,7 @@ async fn process_incoming_peer_message( received_set.insert(validator.clone()); } else { gum::trace!(target: LOG_TARGET, ?validator_index, ?origin, "Duplicate message"); - modify_reputation(ctx, relay_parent, origin, COST_PEER_DUPLICATE_MESSAGE).await; + modify_reputation(ctx.sender(), relay_parent, origin, COST_PEER_DUPLICATE_MESSAGE).await; return }; @@ -560,13 +553,13 @@ async fn process_incoming_peer_message( "already received a message for validator", ); if old_message.signed_availability.as_unchecked() == &bitfield { - modify_reputation(ctx, relay_parent, origin, BENEFIT_VALID_MESSAGE).await; + modify_reputation(ctx.sender(), relay_parent, origin, BENEFIT_VALID_MESSAGE).await; } return } let signed_availability = match bitfield.try_into_checked(&signing_context, &validator) { Err(_) => { - modify_reputation(ctx, relay_parent, origin, COST_SIGNATURE_INVALID).await; + modify_reputation(ctx.sender(), relay_parent, origin, COST_SIGNATURE_INVALID).await; return }, Ok(bitfield) => bitfield, @@ -592,20 +585,19 @@ async fn process_incoming_peer_message( ) .await; - modify_reputation(ctx, relay_parent, origin, BENEFIT_VALID_MESSAGE_FIRST).await + modify_reputation(ctx.sender(), relay_parent, origin, BENEFIT_VALID_MESSAGE_FIRST).await } /// Deal with network bridge updates and track what needs to be tracked /// which depends on the message type received. +#[overseer::contextbounds(BitfieldDistribution, prefix=self::overseer)] async fn handle_network_msg( ctx: &mut Context, state: &mut ProtocolState, metrics: &Metrics, bridge_message: NetworkBridgeEvent, rng: &mut (impl CryptoRng + Rng), -) where - Context: SubsystemContext, -{ +) { let _timer = metrics.time_handle_network_msg(); match bridge_message { @@ -677,15 +669,14 @@ fn handle_our_view_change(state: &mut ProtocolState, view: OurView) { // Send the difference between two views which were not sent // to that particular peer. +#[overseer::contextbounds(BitfieldDistribution, prefix=self::overseer)] async fn handle_peer_view_change( ctx: &mut Context, state: &mut ProtocolState, origin: PeerId, view: View, rng: &mut (impl CryptoRng + Rng), -) where - Context: SubsystemContext, -{ +) { let added = state .peer_views .entry(origin.clone()) @@ -736,15 +727,14 @@ async fn handle_peer_view_change( } /// Send a gossip message and track it in the per relay parent data. +#[overseer::contextbounds(BitfieldDistribution, prefix=self::overseer)] async fn send_tracked_gossip_message( ctx: &mut Context, state: &mut ProtocolState, dest: PeerId, validator: ValidatorId, message: BitfieldGossipMessage, -) where - Context: SubsystemContext, -{ +) { let job_data = if let Some(job_data) = state.per_relay_parent.get_mut(&message.relay_parent) { job_data } else { @@ -773,11 +763,8 @@ async fn send_tracked_gossip_message( .await; } -impl overseer::Subsystem for BitfieldDistribution -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::subsystem(BitfieldDistribution, error=SubsystemError, prefix=self::overseer)] +impl BitfieldDistribution { fn start(self, ctx: Context) -> SpawnedSubsystem { let future = self.run(ctx).map(|_| Ok(())).boxed(); @@ -786,13 +773,11 @@ where } /// Query our validator set and signing context for a particular relay parent. +#[overseer::contextbounds(BitfieldDistribution, prefix=self::overseer)] async fn query_basics( ctx: &mut Context, relay_parent: Hash, -) -> SubsystemResult, SigningContext)>> -where - Context: SubsystemContext, -{ +) -> SubsystemResult, SigningContext)>> { let (validators_tx, validators_rx) = oneshot::channel(); let (session_tx, session_rx) = oneshot::channel(); diff --git a/node/network/bridge/src/lib.rs b/node/network/bridge/src/lib.rs index 860a9b0c5fa2..e11d3500487a 100644 --- a/node/network/bridge/src/lib.rs +++ b/node/network/bridge/src/lib.rs @@ -33,17 +33,18 @@ use polkadot_node_network_protocol::{ v1 as protocol_v1, ObservedRole, OurView, PeerId, ProtocolVersion, UnifiedReputationChange as Rep, Versioned, View, }; + use polkadot_node_subsystem::{ errors::{SubsystemError, SubsystemResult}, messages::{ network_bridge_event::{NewGossipTopology, TopologyPeerInfo}, - AllMessages, CollatorProtocolMessage, NetworkBridgeEvent, NetworkBridgeMessage, + ApprovalDistributionMessage, BitfieldDistributionMessage, CollatorProtocolMessage, + GossipSupportMessage, NetworkBridgeEvent, NetworkBridgeMessage, + StatementDistributionMessage, }, overseer, ActivatedLeaf, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, - SubsystemContext, SubsystemSender, }; -use polkadot_node_subsystem_util::metrics::{self, prometheus}; -use polkadot_overseer::gen::{OverseerError, Subsystem}; +use polkadot_overseer::gen::OverseerError; use polkadot_primitives::v2::{AuthorityDiscoveryId, BlockNumber, Hash, ValidatorIndex}; /// Peer set info for network initialization. @@ -67,6 +68,9 @@ use network::{send_message, Network}; use crate::network::get_peer_id_by_authority_id; +mod metrics; +use self::metrics::Metrics; + #[cfg(test)] mod tests; @@ -83,206 +87,6 @@ const EMPTY_VIEW_COST: Rep = Rep::CostMajor("Peer sent us an empty view"); // network bridge log target const LOG_TARGET: &'static str = "parachain::network-bridge"; -/// Metrics for the network bridge. -#[derive(Clone, Default)] -pub struct Metrics(Option); - -fn peer_set_label(peer_set: PeerSet, version: ProtocolVersion) -> &'static str { - // Higher level code is meant to protect against this ever happening. - peer_set.get_protocol_name_static(version).unwrap_or("") -} - -impl Metrics { - fn on_peer_connected(&self, peer_set: PeerSet, version: ProtocolVersion) { - self.0.as_ref().map(|metrics| { - metrics - .connected_events - .with_label_values(&[peer_set_label(peer_set, version)]) - .inc() - }); - } - - fn on_peer_disconnected(&self, peer_set: PeerSet, version: ProtocolVersion) { - self.0.as_ref().map(|metrics| { - metrics - .disconnected_events - .with_label_values(&[peer_set_label(peer_set, version)]) - .inc() - }); - } - - fn note_peer_count(&self, peer_set: PeerSet, version: ProtocolVersion, count: usize) { - self.0.as_ref().map(|metrics| { - metrics - .peer_count - .with_label_values(&[peer_set_label(peer_set, version)]) - .set(count as u64) - }); - } - - fn on_notification_received(&self, peer_set: PeerSet, version: ProtocolVersion, size: usize) { - if let Some(metrics) = self.0.as_ref() { - metrics - .notifications_received - .with_label_values(&[peer_set_label(peer_set, version)]) - .inc(); - - metrics - .bytes_received - .with_label_values(&[peer_set_label(peer_set, version)]) - .inc_by(size as u64); - } - } - - fn on_notification_sent( - &self, - peer_set: PeerSet, - version: ProtocolVersion, - size: usize, - to_peers: usize, - ) { - if let Some(metrics) = self.0.as_ref() { - metrics - .notifications_sent - .with_label_values(&[peer_set_label(peer_set, version)]) - .inc_by(to_peers as u64); - - metrics - .bytes_sent - .with_label_values(&[peer_set_label(peer_set, version)]) - .inc_by((size * to_peers) as u64); - } - } - - fn note_desired_peer_count(&self, peer_set: PeerSet, size: usize) { - self.0.as_ref().map(|metrics| { - metrics - .desired_peer_count - .with_label_values(&[peer_set.get_default_protocol_name()]) - .set(size as u64) - }); - } - - fn on_report_event(&self) { - if let Some(metrics) = self.0.as_ref() { - metrics.report_events.inc() - } - } -} - -#[derive(Clone)] -struct MetricsInner { - peer_count: prometheus::GaugeVec, - connected_events: prometheus::CounterVec, - disconnected_events: prometheus::CounterVec, - desired_peer_count: prometheus::GaugeVec, - report_events: prometheus::Counter, - - notifications_received: prometheus::CounterVec, - notifications_sent: prometheus::CounterVec, - - bytes_received: prometheus::CounterVec, - bytes_sent: prometheus::CounterVec, -} - -impl metrics::Metrics for Metrics { - fn try_register( - registry: &prometheus::Registry, - ) -> std::result::Result { - let metrics = MetricsInner { - peer_count: prometheus::register( - prometheus::GaugeVec::new( - prometheus::Opts::new( - "polkadot_parachain_peer_count", - "The number of peers on a parachain-related peer-set", - ), - &["protocol"] - )?, - registry, - )?, - connected_events: prometheus::register( - prometheus::CounterVec::new( - prometheus::Opts::new( - "polkadot_parachain_peer_connect_events_total", - "The number of peer connect events on a parachain notifications protocol", - ), - &["protocol"] - )?, - registry, - )?, - disconnected_events: prometheus::register( - prometheus::CounterVec::new( - prometheus::Opts::new( - "polkadot_parachain_peer_disconnect_events_total", - "The number of peer disconnect events on a parachain notifications protocol", - ), - &["protocol"] - )?, - registry, - )?, - desired_peer_count: prometheus::register( - prometheus::GaugeVec::new( - prometheus::Opts::new( - "polkadot_parachain_desired_peer_count", - "The number of peers that the local node is expected to connect to on a parachain-related peer-set (either including or not including unresolvable authorities, depending on whether `ConnectToValidators` or `ConnectToValidatorsResolved` was used.)", - ), - &["protocol"] - )?, - registry, - )?, - report_events: prometheus::register( - prometheus::Counter::new( - "polkadot_parachain_network_report_events_total", - "The amount of reputation changes issued by subsystems", - )?, - registry, - )?, - notifications_received: prometheus::register( - prometheus::CounterVec::new( - prometheus::Opts::new( - "polkadot_parachain_notifications_received_total", - "The number of notifications received on a parachain protocol", - ), - &["protocol"] - )?, - registry, - )?, - notifications_sent: prometheus::register( - prometheus::CounterVec::new( - prometheus::Opts::new( - "polkadot_parachain_notifications_sent_total", - "The number of notifications sent on a parachain protocol", - ), - &["protocol"] - )?, - registry, - )?, - bytes_received: prometheus::register( - prometheus::CounterVec::new( - prometheus::Opts::new( - "polkadot_parachain_notification_bytes_received_total", - "The number of bytes received on a parachain notification protocol", - ), - &["protocol"] - )?, - registry, - )?, - bytes_sent: prometheus::register( - prometheus::CounterVec::new( - prometheus::Opts::new( - "polkadot_parachain_notification_bytes_sent_total", - "The number of bytes sent on a parachain notification protocol", - ), - &["protocol"] - )?, - registry, - )?, - }; - - Ok(Metrics(Some(metrics))) - } -} - /// Messages from and to the network. /// /// As transmitted to and received from subsystems. @@ -320,12 +124,11 @@ impl NetworkBridge { } } -impl Subsystem for NetworkBridge +#[overseer::subsystem(NetworkBridge, error = SubsystemError, prefix = self::overseer)] +impl NetworkBridge where Net: Network + Sync, AD: validator_discovery::AuthorityDiscovery + Clone, - Context: SubsystemContext - + overseer::SubsystemContext, { fn start(mut self, ctx: Context) -> SpawnedSubsystem { // The stream of networking events has to be created at initialization, otherwise the @@ -382,6 +185,7 @@ enum Mode { Active, } +#[overseer::contextbounds(NetworkBridge, prefix = self::overseer)] async fn handle_subsystem_messages( mut ctx: Context, mut network_service: N, @@ -391,8 +195,6 @@ async fn handle_subsystem_messages( metrics: Metrics, ) -> Result<(), UnexpectedAbort> where - Context: SubsystemContext, - Context: overseer::SubsystemContext, N: Network, AD: validator_discovery::AuthorityDiscovery + Clone, { @@ -678,7 +480,7 @@ where } async fn handle_network_messages( - mut sender: impl SubsystemSender, + mut sender: impl overseer::NetworkBridgeSenderTrait, mut network_service: impl Network, network_stream: BoxStream<'static, NetworkEvent>, mut authority_discovery_service: AD, @@ -1031,6 +833,7 @@ async fn handle_network_messages( /// #fn is_send(); /// #is_send::(); /// ``` +#[overseer::contextbounds(NetworkBridge, prefix = self::overseer)] async fn run_network( bridge: NetworkBridge, mut ctx: Context, @@ -1039,8 +842,6 @@ async fn run_network( where N: Network, AD: validator_discovery::AuthorityDiscovery + Clone, - Context: SubsystemContext - + overseer::SubsystemContext, { let shared = Shared::default(); @@ -1105,14 +906,17 @@ fn construct_view( View::new(live_heads.take(MAX_VIEW_HEADS), finalized_number) } -fn update_our_view( - net: &mut impl Network, - ctx: &mut impl SubsystemContext, +#[overseer::contextbounds(NetworkBridge, prefix = self::overseer)] +fn update_our_view( + net: &mut Net, + ctx: &mut Context, live_heads: &[ActivatedLeaf], shared: &Shared, finalized_number: BlockNumber, metrics: &Metrics, -) { +) where + Net: Network, +{ let new_view = construct_view(live_heads.iter().map(|v| v.hash), finalized_number); let (validation_peers, collation_peers) = { @@ -1238,54 +1042,79 @@ fn send_collation_message_v1( async fn dispatch_validation_event_to_all( event: NetworkBridgeEvent, - ctx: &mut impl SubsystemSender, + ctx: &mut impl overseer::NetworkBridgeSenderTrait, ) { dispatch_validation_events_to_all(std::iter::once(event), ctx).await } async fn dispatch_collation_event_to_all( event: NetworkBridgeEvent, - ctx: &mut impl SubsystemSender, + ctx: &mut impl overseer::NetworkBridgeSenderTrait, ) { dispatch_collation_events_to_all(std::iter::once(event), ctx).await } fn dispatch_validation_event_to_all_unbounded( event: NetworkBridgeEvent, - ctx: &mut impl SubsystemSender, + sender: &mut impl overseer::NetworkBridgeSenderTrait, ) { - for msg in AllMessages::dispatch_iter(event) { - ctx.send_unbounded_message(msg); - } + event + .focus() + .ok() + .map(StatementDistributionMessage::from) + .and_then(|msg| Some(sender.send_unbounded_message(msg))); + event + .focus() + .ok() + .map(BitfieldDistributionMessage::from) + .and_then(|msg| Some(sender.send_unbounded_message(msg))); + event + .focus() + .ok() + .map(ApprovalDistributionMessage::from) + .and_then(|msg| Some(sender.send_unbounded_message(msg))); + event + .focus() + .ok() + .map(GossipSupportMessage::from) + .and_then(|msg| Some(sender.send_unbounded_message(msg))); } fn dispatch_collation_event_to_all_unbounded( event: NetworkBridgeEvent, - ctx: &mut impl SubsystemSender, + sender: &mut impl overseer::NetworkBridgeSenderTrait, ) { - if let Some(msg) = event.focus().ok().map(CollatorProtocolMessage::NetworkBridgeUpdate) { - ctx.send_unbounded_message(msg.into()); + if let Ok(msg) = event.focus() { + sender.send_unbounded_message(CollatorProtocolMessage::NetworkBridgeUpdate(msg)) } } -async fn dispatch_validation_events_to_all(events: I, ctx: &mut impl SubsystemSender) -where +async fn dispatch_validation_events_to_all( + events: I, + sender: &mut impl overseer::NetworkBridgeSenderTrait, +) where I: IntoIterator>, I::IntoIter: Send, { - ctx.send_messages(events.into_iter().flat_map(AllMessages::dispatch_iter)).await + for event in events { + sender + .send_messages(event.focus().map(StatementDistributionMessage::from)) + .await; + sender.send_messages(event.focus().map(BitfieldDistributionMessage::from)).await; + sender.send_messages(event.focus().map(ApprovalDistributionMessage::from)).await; + sender.send_messages(event.focus().map(GossipSupportMessage::from)).await; + } } -async fn dispatch_collation_events_to_all(events: I, ctx: &mut impl SubsystemSender) -where +async fn dispatch_collation_events_to_all( + events: I, + ctx: &mut impl overseer::NetworkBridgeSenderTrait, +) where I: IntoIterator>, I::IntoIter: Send, { let messages_for = |event: NetworkBridgeEvent| { - event - .focus() - .ok() - .map(|m| AllMessages::CollatorProtocol(CollatorProtocolMessage::NetworkBridgeUpdate(m))) + event.focus().ok().map(|m| CollatorProtocolMessage::NetworkBridgeUpdate(m)) }; ctx.send_messages(events.into_iter().flat_map(messages_for)).await diff --git a/node/network/bridge/src/metrics.rs b/node/network/bridge/src/metrics.rs new file mode 100644 index 000000000000..4ecdd5bd6f13 --- /dev/null +++ b/node/network/bridge/src/metrics.rs @@ -0,0 +1,223 @@ +// Copyright 2022 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 . + +use super::{PeerSet, ProtocolVersion}; +use polkadot_node_subsystem_util::metrics::{self, prometheus}; + +/// Metrics for the network bridge. +#[derive(Clone, Default)] +pub struct Metrics(pub(crate) Option); + +fn peer_set_label(peer_set: PeerSet, version: ProtocolVersion) -> &'static str { + // Higher level code is meant to protect against this ever happening. + peer_set.get_protocol_name_static(version).unwrap_or("") +} + +impl Metrics { + pub fn on_peer_connected(&self, peer_set: PeerSet, version: ProtocolVersion) { + self.0.as_ref().map(|metrics| { + metrics + .connected_events + .with_label_values(&[peer_set_label(peer_set, version)]) + .inc() + }); + } + + pub fn on_peer_disconnected(&self, peer_set: PeerSet, version: ProtocolVersion) { + self.0.as_ref().map(|metrics| { + metrics + .disconnected_events + .with_label_values(&[peer_set_label(peer_set, version)]) + .inc() + }); + } + + pub fn note_peer_count(&self, peer_set: PeerSet, version: ProtocolVersion, count: usize) { + self.0.as_ref().map(|metrics| { + metrics + .peer_count + .with_label_values(&[peer_set_label(peer_set, version)]) + .set(count as u64) + }); + } + + pub fn on_notification_received( + &self, + peer_set: PeerSet, + version: ProtocolVersion, + size: usize, + ) { + if let Some(metrics) = self.0.as_ref() { + metrics + .notifications_received + .with_label_values(&[peer_set_label(peer_set, version)]) + .inc(); + + metrics + .bytes_received + .with_label_values(&[peer_set_label(peer_set, version)]) + .inc_by(size as u64); + } + } + + pub fn on_notification_sent( + &self, + peer_set: PeerSet, + version: ProtocolVersion, + size: usize, + to_peers: usize, + ) { + if let Some(metrics) = self.0.as_ref() { + metrics + .notifications_sent + .with_label_values(&[peer_set_label(peer_set, version)]) + .inc_by(to_peers as u64); + + metrics + .bytes_sent + .with_label_values(&[peer_set_label(peer_set, version)]) + .inc_by((size * to_peers) as u64); + } + } + + pub fn note_desired_peer_count(&self, peer_set: PeerSet, size: usize) { + self.0.as_ref().map(|metrics| { + metrics + .desired_peer_count + .with_label_values(&[peer_set.get_default_protocol_name()]) + .set(size as u64) + }); + } + + pub fn on_report_event(&self) { + if let Some(metrics) = self.0.as_ref() { + metrics.report_events.inc() + } + } +} + +#[derive(Clone)] +pub(crate) struct MetricsInner { + peer_count: prometheus::GaugeVec, + connected_events: prometheus::CounterVec, + disconnected_events: prometheus::CounterVec, + desired_peer_count: prometheus::GaugeVec, + report_events: prometheus::Counter, + + notifications_received: prometheus::CounterVec, + notifications_sent: prometheus::CounterVec, + + bytes_received: prometheus::CounterVec, + bytes_sent: prometheus::CounterVec, +} + +impl metrics::Metrics for Metrics { + fn try_register( + registry: &prometheus::Registry, + ) -> std::result::Result { + let metrics = MetricsInner { + peer_count: prometheus::register( + prometheus::GaugeVec::new( + prometheus::Opts::new( + "polkadot_parachain_peer_count", + "The number of peers on a parachain-related peer-set", + ), + &["protocol"] + )?, + registry, + )?, + connected_events: prometheus::register( + prometheus::CounterVec::new( + prometheus::Opts::new( + "polkadot_parachain_peer_connect_events_total", + "The number of peer connect events on a parachain notifications protocol", + ), + &["protocol"] + )?, + registry, + )?, + disconnected_events: prometheus::register( + prometheus::CounterVec::new( + prometheus::Opts::new( + "polkadot_parachain_peer_disconnect_events_total", + "The number of peer disconnect events on a parachain notifications protocol", + ), + &["protocol"] + )?, + registry, + )?, + desired_peer_count: prometheus::register( + prometheus::GaugeVec::new( + prometheus::Opts::new( + "polkadot_parachain_desired_peer_count", + "The number of peers that the local node is expected to connect to on a parachain-related peer-set (either including or not including unresolvable authorities, depending on whether `ConnectToValidators` or `ConnectToValidatorsResolved` was used.)", + ), + &["protocol"] + )?, + registry, + )?, + report_events: prometheus::register( + prometheus::Counter::new( + "polkadot_parachain_network_report_events_total", + "The amount of reputation changes issued by subsystems", + )?, + registry, + )?, + notifications_received: prometheus::register( + prometheus::CounterVec::new( + prometheus::Opts::new( + "polkadot_parachain_notifications_received_total", + "The number of notifications received on a parachain protocol", + ), + &["protocol"] + )?, + registry, + )?, + notifications_sent: prometheus::register( + prometheus::CounterVec::new( + prometheus::Opts::new( + "polkadot_parachain_notifications_sent_total", + "The number of notifications sent on a parachain protocol", + ), + &["protocol"] + )?, + registry, + )?, + bytes_received: prometheus::register( + prometheus::CounterVec::new( + prometheus::Opts::new( + "polkadot_parachain_notification_bytes_received_total", + "The number of bytes received on a parachain notification protocol", + ), + &["protocol"] + )?, + registry, + )?, + bytes_sent: prometheus::register( + prometheus::CounterVec::new( + prometheus::Opts::new( + "polkadot_parachain_notification_bytes_sent_total", + "The number of bytes sent on a parachain notification protocol", + ), + &["protocol"] + )?, + registry, + )?, + }; + + Ok(Metrics(Some(metrics))) + } +} diff --git a/node/network/bridge/src/tests.rs b/node/network/bridge/src/tests.rs index 0697c0cfdea8..09c7f270c11f 100644 --- a/node/network/bridge/src/tests.rs +++ b/node/network/bridge/src/tests.rs @@ -34,8 +34,8 @@ use polkadot_node_network_protocol::{ use polkadot_node_subsystem::{ jaeger, messages::{ - ApprovalDistributionMessage, BitfieldDistributionMessage, GossipSupportMessage, - StatementDistributionMessage, + AllMessages, ApprovalDistributionMessage, BitfieldDistributionMessage, + GossipSupportMessage, StatementDistributionMessage, }, ActiveLeavesUpdate, FromOverseer, LeafStatus, OverseerSignal, }; @@ -313,8 +313,9 @@ async fn assert_sends_validation_event_to_all( event: NetworkBridgeEvent, virtual_overseer: &mut TestSubsystemContextHandle, ) { - // Ordering must match the enum variant order - // in `AllMessages`. + // Ordering must be consistent across: + // `fn dispatch_validation_event_to_all_unbounded` + // `dispatch_validation_events_to_all` assert_matches!( virtual_overseer.recv().await, AllMessages::StatementDistribution( @@ -1190,54 +1191,6 @@ fn send_messages_to_peers() { }); } -#[test] -fn spread_event_to_subsystems_is_up_to_date() { - // Number of subsystems expected to be interested in a network event, - // and hence the network event broadcasted to. - const EXPECTED_COUNT: usize = 4; - - let mut cnt = 0_usize; - for msg in AllMessages::dispatch_iter(NetworkBridgeEvent::PeerDisconnected(PeerId::random())) { - match msg { - AllMessages::Empty => unreachable!("Nobody cares about the dummy"), - AllMessages::CandidateValidation(_) => unreachable!("Not interested in network events"), - AllMessages::CandidateBacking(_) => unreachable!("Not interested in network events"), - AllMessages::ChainApi(_) => unreachable!("Not interested in network events"), - AllMessages::CollatorProtocol(_) => unreachable!("Not interested in network events"), - AllMessages::StatementDistribution(_) => { - cnt += 1; - }, - AllMessages::AvailabilityDistribution(_) => - unreachable!("Not interested in network events"), - AllMessages::AvailabilityRecovery(_) => - unreachable!("Not interested in network events"), - AllMessages::BitfieldDistribution(_) => { - cnt += 1; - }, - AllMessages::BitfieldSigning(_) => unreachable!("Not interested in network events"), - AllMessages::Provisioner(_) => unreachable!("Not interested in network events"), - AllMessages::RuntimeApi(_) => unreachable!("Not interested in network events"), - AllMessages::AvailabilityStore(_) => unreachable!("Not interested in network events"), - AllMessages::NetworkBridge(_) => unreachable!("Not interested in network events"), - AllMessages::CollationGeneration(_) => unreachable!("Not interested in network events"), - AllMessages::ApprovalVoting(_) => unreachable!("Not interested in network events"), - AllMessages::ApprovalDistribution(_) => { - cnt += 1; - }, - AllMessages::GossipSupport(_) => { - cnt += 1; - }, - AllMessages::DisputeCoordinator(_) => unreachable!("Not interested in network events"), - AllMessages::DisputeDistribution(_) => unreachable!("Not interested in network events"), - AllMessages::ChainSelection(_) => unreachable!("Not interested in network events"), - AllMessages::PvfChecker(_) => unreachable!("Not interested in network events"), - // Add variants here as needed, `{ cnt += 1; }` for those that need to be - // notified, `unreachable!()` for those that should not. - } - } - assert_eq!(cnt, EXPECTED_COUNT); -} - #[test] fn our_view_updates_decreasing_order_and_limited_to_max() { test_harness(done_syncing_oracle(), |test_harness| async move { diff --git a/node/network/collator-protocol/src/collator_side/mod.rs b/node/network/collator-protocol/src/collator_side/mod.rs index 6f23600ae8f3..767d05aba357 100644 --- a/node/network/collator-protocol/src/collator_side/mod.rs +++ b/node/network/collator-protocol/src/collator_side/mod.rs @@ -38,8 +38,10 @@ use polkadot_node_network_protocol::{ use polkadot_node_primitives::{CollationSecondedSignal, PoV, Statement}; use polkadot_node_subsystem::{ jaeger, - messages::{CollatorProtocolMessage, NetworkBridgeEvent, NetworkBridgeMessage}, - overseer, FromOverseer, OverseerSignal, PerLeafSpan, SubsystemContext, + messages::{ + CollatorProtocolMessage, NetworkBridgeEvent, NetworkBridgeMessage, RuntimeApiMessage, + }, + overseer, FromOverseer, OverseerSignal, PerLeafSpan, }; use polkadot_node_subsystem_util::{ metrics::{self, prometheus}, @@ -360,6 +362,7 @@ impl State { /// or the relay-parent isn't in the active-leaves set, we ignore the message /// as it must be invalid in that case - although this indicates a logic error /// elsewhere in the node. +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] async fn distribute_collation( ctx: &mut Context, runtime: &mut RuntimeInfo, @@ -368,11 +371,7 @@ async fn distribute_collation( receipt: CandidateReceipt, pov: PoV, result_sender: Option>, -) -> Result<()> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> Result<()> { let relay_parent = receipt.descriptor.relay_parent; // This collation is not in the active-leaves set. @@ -398,7 +397,7 @@ where // Determine which core the para collated-on is assigned to. // If it is not scheduled then ignore the message. - let (our_core, num_cores) = match determine_core(ctx, id, relay_parent).await? { + let (our_core, num_cores) = match determine_core(ctx.sender(), id, relay_parent).await? { Some(core) => core, None => { gum::warn!( @@ -461,16 +460,12 @@ where /// Get the Id of the Core that is assigned to the para being collated on if any /// and the total number of cores. -async fn determine_core( - ctx: &mut Context, +async fn determine_core( + sender: &mut impl overseer::SubsystemSender, para_id: ParaId, relay_parent: Hash, -) -> Result> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ - let cores = get_availability_cores(ctx, relay_parent).await?; +) -> Result> { + let cores = get_availability_cores(sender, relay_parent).await?; for (idx, core) in cores.iter().enumerate() { if let CoreState::Scheduled(occupied) = core { @@ -493,17 +488,14 @@ struct GroupValidators { /// Figure out current group of validators assigned to the para being collated on. /// /// Returns [`ValidatorId`]'s of current group as determined based on the `relay_parent`. +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] async fn determine_our_validators( ctx: &mut Context, runtime: &mut RuntimeInfo, core_index: CoreIndex, cores: usize, relay_parent: Hash, -) -> Result -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> Result { let session_index = runtime.get_session_index_for_child(ctx.sender(), relay_parent).await?; let info = &runtime .get_session_info_by_index(ctx.sender(), relay_parent, session_index) @@ -511,7 +503,7 @@ where .session_info; gum::debug!(target: LOG_TARGET, ?session_index, "Received session info"); let groups = &info.validator_groups; - let rotation_info = get_group_rotation_info(ctx, relay_parent).await?; + let rotation_info = get_group_rotation_info(ctx.sender(), relay_parent).await?; let current_group_index = rotation_info.group_for_core(core_index, cores); let current_validators = groups @@ -530,11 +522,8 @@ where } /// Issue a `Declare` collation message to the given `peer`. -async fn declare(ctx: &mut Context, state: &mut State, peer: PeerId) -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] +async fn declare(ctx: &mut Context, state: &mut State, peer: PeerId) { let declare_signature_payload = protocol_v1::declare_signature_payload(&state.local_peer_id); if let Some(para_id) = state.collating_on { @@ -554,11 +543,11 @@ where /// Issue a connection request to a set of validators and /// revoke the previous connection request. -async fn connect_to_validators(ctx: &mut Context, validator_ids: Vec) -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] +async fn connect_to_validators( + ctx: &mut Context, + validator_ids: Vec, +) { // ignore address resolution failure // will reissue a new request on new collation let (failed, _) = oneshot::channel(); @@ -574,15 +563,13 @@ where /// /// This will only advertise a collation if there exists one for the given `relay_parent` and the given `peer` is /// set as validator for our para at the given `relay_parent`. +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] async fn advertise_collation( ctx: &mut Context, state: &mut State, relay_parent: Hash, peer: PeerId, -) where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) { let should_advertise = state .our_validators_groups .get(&relay_parent) @@ -635,16 +622,13 @@ async fn advertise_collation( } /// The main incoming message dispatching switch. +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] async fn process_msg( ctx: &mut Context, runtime: &mut RuntimeInfo, state: &mut State, msg: CollatorProtocolMessage, -) -> Result<()> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> Result<()> { use CollatorProtocolMessage::*; match msg { @@ -748,17 +732,14 @@ async fn send_collation( } /// A networking messages switch. +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] async fn handle_incoming_peer_message( ctx: &mut Context, runtime: &mut RuntimeInfo, state: &mut State, origin: PeerId, msg: protocol_v1::CollatorProtocolMessage, -) -> Result<()> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> Result<()> { use protocol_v1::CollatorProtocolMessage::*; match msg { @@ -831,15 +812,12 @@ where } /// Process an incoming network request for a collation. +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] async fn handle_incoming_request( ctx: &mut Context, state: &mut State, req: IncomingRequest, -) -> Result<()> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> Result<()> { let _span = state .span_per_relay_parent .get(&req.payload.relay_parent) @@ -907,15 +885,13 @@ where } /// Our view has changed. +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] async fn handle_peer_view_change( ctx: &mut Context, state: &mut State, peer_id: PeerId, view: View, -) where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) { let current = state.peer_views.entry(peer_id.clone()).or_default(); let added: Vec = view.difference(&*current).cloned().collect(); @@ -928,16 +904,13 @@ async fn handle_peer_view_change( } /// Bridge messages switch. +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] async fn handle_network_msg( ctx: &mut Context, runtime: &mut RuntimeInfo, state: &mut State, bridge_message: NetworkBridgeEvent, -) -> Result<()> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> Result<()> { use NetworkBridgeEvent::*; match bridge_message { @@ -1021,17 +994,14 @@ async fn handle_our_view_change(state: &mut State, view: OurView) -> Result<()> } /// The collator protocol collator side main loop. +#[overseer::contextbounds(CollatorProtocol, prefix = crate::overseer)] pub(crate) async fn run( mut ctx: Context, local_peer_id: PeerId, collator_pair: CollatorPair, mut req_receiver: IncomingRequestReceiver, metrics: Metrics, -) -> std::result::Result<(), FatalError> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> std::result::Result<(), FatalError> { use OverseerSignal::*; let mut state = State::new(local_peer_id, collator_pair, metrics); diff --git a/node/network/collator-protocol/src/lib.rs b/node/network/collator-protocol/src/lib.rs index 21a5fd9baafd..bdf8904b7d07 100644 --- a/node/network/collator-protocol/src/lib.rs +++ b/node/network/collator-protocol/src/lib.rs @@ -34,9 +34,7 @@ use polkadot_node_network_protocol::{ use polkadot_primitives::v2::CollatorPair; use polkadot_node_subsystem::{ - errors::SubsystemError, - messages::{CollatorProtocolMessage, NetworkBridgeMessage}, - overseer, SpawnedSubsystem, SubsystemContext, SubsystemSender, + errors::SubsystemError, messages::NetworkBridgeMessage, overseer, SpawnedSubsystem, }; mod error; @@ -89,6 +87,7 @@ pub struct CollatorProtocolSubsystem { protocol_side: ProtocolSide, } +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] impl CollatorProtocolSubsystem { /// Start the collator protocol. /// If `id` is `Some` this is a collator side of the protocol. @@ -98,11 +97,7 @@ impl CollatorProtocolSubsystem { Self { protocol_side } } - async fn run(self, ctx: Context) -> std::result::Result<(), error::FatalError> - where - Context: overseer::SubsystemContext, - Context: SubsystemContext, - { + async fn run(self, ctx: Context) -> std::result::Result<(), error::FatalError> { match self.protocol_side { ProtocolSide::Validator { keystore, eviction_policy, metrics } => validator_side::run(ctx, keystore, eviction_policy, metrics).await, @@ -112,12 +107,8 @@ impl CollatorProtocolSubsystem { } } -impl overseer::Subsystem for CollatorProtocolSubsystem -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, - ::Sender: SubsystemSender, -{ +#[overseer::subsystem(CollatorProtocol, error=SubsystemError, prefix=self::overseer)] +impl CollatorProtocolSubsystem { fn start(self, ctx: Context) -> SpawnedSubsystem { let future = self .run(ctx) @@ -129,10 +120,11 @@ where } /// Modify the reputation of a peer based on its behavior. -async fn modify_reputation(ctx: &mut Context, peer: PeerId, rep: Rep) -where - Context: SubsystemContext, -{ +async fn modify_reputation( + sender: &mut impl overseer::CollatorProtocolSenderTrait, + peer: PeerId, + rep: Rep, +) { gum::trace!( target: LOG_TARGET, rep = ?rep, @@ -140,5 +132,5 @@ where "reputation change for peer", ); - ctx.send_message(NetworkBridgeMessage::ReportPeer(peer, rep)).await; + sender.send_message(NetworkBridgeMessage::ReportPeer(peer, rep)).await; } diff --git a/node/network/collator-protocol/src/validator_side/mod.rs b/node/network/collator-protocol/src/validator_side/mod.rs index 8b91e8ef25a1..3ebc253edb78 100644 --- a/node/network/collator-protocol/src/validator_side/mod.rs +++ b/node/network/collator-protocol/src/validator_side/mod.rs @@ -48,9 +48,9 @@ use polkadot_node_subsystem::{ jaeger, messages::{ CandidateBackingMessage, CollatorProtocolMessage, IfDisconnected, NetworkBridgeEvent, - NetworkBridgeMessage, + NetworkBridgeMessage, RuntimeApiMessage, }, - overseer, FromOverseer, OverseerSignal, PerLeafSpan, SubsystemContext, SubsystemSender, + overseer, FromOverseer, OverseerSignal, PerLeafSpan, SubsystemSender, }; use polkadot_node_subsystem_util::metrics::{self, prometheus}; use polkadot_primitives::v2::{CandidateReceipt, CollatorId, Hash, Id as ParaId}; @@ -362,7 +362,7 @@ struct ActiveParas { impl ActiveParas { async fn assign_incoming( &mut self, - sender: &mut impl SubsystemSender, + sender: &mut impl SubsystemSender, keystore: &SyncCryptoStorePtr, new_relay_parents: impl IntoIterator, ) { @@ -630,25 +630,19 @@ fn collator_peer_id( }) } -async fn disconnect_peer(ctx: &mut Context, peer_id: PeerId) -where - Context: overseer::SubsystemContext, - Context: SubsystemContext, -{ - ctx.send_message(NetworkBridgeMessage::DisconnectPeer(peer_id, PeerSet::Collation)) +async fn disconnect_peer(sender: &mut impl overseer::CollatorProtocolSenderTrait, peer_id: PeerId) { + sender + .send_message(NetworkBridgeMessage::DisconnectPeer(peer_id, PeerSet::Collation)) .await } /// Another subsystem has requested to fetch collations on a particular leaf for some para. -async fn fetch_collation( - ctx: &mut Context, +async fn fetch_collation( + sender: &mut impl overseer::CollatorProtocolSenderTrait, state: &mut State, pc: PendingCollation, id: CollatorId, -) where - Context: overseer::SubsystemContext, - Context: SubsystemContext, -{ +) { let (tx, rx) = oneshot::channel(); let PendingCollation { relay_parent, para_id, peer_id, .. } = pc; @@ -663,7 +657,7 @@ async fn fetch_collation( if let Some(peer_data) = state.peer_data.get(&peer_id) { if peer_data.has_advertised(&relay_parent) { - request_collation(ctx, state, relay_parent, para_id, peer_id, tx).await; + request_collation(sender, state, relay_parent, para_id, peer_id, tx).await; } else { gum::debug!( target: LOG_TARGET, @@ -687,51 +681,44 @@ async fn fetch_collation( } /// Report a collator for some malicious actions. -async fn report_collator( - ctx: &mut Context, +async fn report_collator( + sender: &mut impl overseer::CollatorProtocolSenderTrait, peer_data: &HashMap, id: CollatorId, -) where - Context: SubsystemContext, -{ +) { if let Some(peer_id) = collator_peer_id(peer_data, &id) { - modify_reputation(ctx, peer_id, COST_REPORT_BAD).await; + modify_reputation(sender, peer_id, COST_REPORT_BAD).await; } } /// Some other subsystem has reported a collator as a good one, bump reputation. -async fn note_good_collation( - ctx: &mut Context, +async fn note_good_collation( + sender: &mut impl overseer::CollatorProtocolSenderTrait, peer_data: &HashMap, id: CollatorId, -) where - Context: overseer::SubsystemContext, - Context: SubsystemContext, -{ +) { if let Some(peer_id) = collator_peer_id(peer_data, &id) { - modify_reputation(ctx, peer_id, BENEFIT_NOTIFY_GOOD).await; + modify_reputation(sender, peer_id, BENEFIT_NOTIFY_GOOD).await; } } /// Notify a collator that its collation got seconded. -async fn notify_collation_seconded( - ctx: &mut Context, +async fn notify_collation_seconded( + sender: &mut impl overseer::CollatorProtocolSenderTrait, peer_id: PeerId, relay_parent: Hash, statement: SignedFullStatement, -) where - Context: overseer::SubsystemContext, - Context: SubsystemContext, -{ +) { let wire_message = protocol_v1::CollatorProtocolMessage::CollationSeconded(relay_parent, statement.into()); - ctx.send_message(NetworkBridgeMessage::SendCollationMessage( - vec![peer_id], - Versioned::V1(protocol_v1::CollationProtocol::CollatorProtocol(wire_message)), - )) - .await; + sender + .send_message(NetworkBridgeMessage::SendCollationMessage( + vec![peer_id], + Versioned::V1(protocol_v1::CollationProtocol::CollatorProtocol(wire_message)), + )) + .await; - modify_reputation(ctx, peer_id, BENEFIT_NOTIFY_GOOD).await; + modify_reputation(sender, peer_id, BENEFIT_NOTIFY_GOOD).await; } /// A peer's view has changed. A number of things should be done: @@ -754,17 +741,14 @@ async fn handle_peer_view_change(state: &mut State, peer_id: PeerId, view: View) /// - Check if the requested collation is in our view. /// - Update `PerRequest` records with the `result` field if necessary. /// And as such invocations of this function may rely on that. -async fn request_collation( - ctx: &mut Context, +async fn request_collation( + sender: &mut impl overseer::CollatorProtocolSenderTrait, state: &mut State, relay_parent: Hash, para_id: ParaId, peer_id: PeerId, result: oneshot::Sender<(CandidateReceipt, PoV)>, -) where - Context: overseer::SubsystemContext, - Context: SubsystemContext, -{ +) { if !state.view.contains(&relay_parent) { gum::debug!( target: LOG_TARGET, @@ -815,29 +799,28 @@ async fn request_collation( "Requesting collation", ); - ctx.send_message(NetworkBridgeMessage::SendRequests( - vec![requests], - IfDisconnected::ImmediateError, - )) - .await; + sender + .send_message(NetworkBridgeMessage::SendRequests( + vec![requests], + IfDisconnected::ImmediateError, + )) + .await; } /// Networking message has been received. +#[overseer::contextbounds(CollatorProtocol, prefix = overseer)] async fn process_incoming_peer_message( ctx: &mut Context, state: &mut State, origin: PeerId, msg: protocol_v1::CollatorProtocolMessage, -) where - Context: overseer::SubsystemContext, - Context: SubsystemContext, -{ +) { use protocol_v1::CollatorProtocolMessage::*; use sp_runtime::traits::AppVerify; match msg { Declare(collator_id, para_id, signature) => { if collator_peer_id(&state.peer_data, &collator_id).is_some() { - modify_reputation(ctx, origin, COST_UNEXPECTED_MESSAGE).await; + modify_reputation(ctx.sender(), origin, COST_UNEXPECTED_MESSAGE).await; return } @@ -850,7 +833,7 @@ async fn process_incoming_peer_message( ?para_id, "Unknown peer", ); - modify_reputation(ctx, origin, COST_UNEXPECTED_MESSAGE).await; + modify_reputation(ctx.sender(), origin, COST_UNEXPECTED_MESSAGE).await; return }, }; @@ -862,7 +845,7 @@ async fn process_incoming_peer_message( ?para_id, "Peer is not in the collating state", ); - modify_reputation(ctx, origin, COST_UNEXPECTED_MESSAGE).await; + modify_reputation(ctx.sender(), origin, COST_UNEXPECTED_MESSAGE).await; return } @@ -873,7 +856,7 @@ async fn process_incoming_peer_message( ?para_id, "Signature verification failure", ); - modify_reputation(ctx, origin, COST_INVALID_SIGNATURE).await; + modify_reputation(ctx.sender(), origin, COST_INVALID_SIGNATURE).await; return } @@ -896,9 +879,9 @@ async fn process_incoming_peer_message( "Declared as collator for unneeded para", ); - modify_reputation(ctx, origin.clone(), COST_UNNEEDED_COLLATOR).await; + modify_reputation(ctx.sender(), origin.clone(), COST_UNNEEDED_COLLATOR).await; gum::trace!(target: LOG_TARGET, "Disconnecting unneeded collator"); - disconnect_peer(ctx, origin).await; + disconnect_peer(ctx.sender(), origin).await; } }, AdvertiseCollation(relay_parent) => { @@ -914,7 +897,7 @@ async fn process_incoming_peer_message( "Advertise collation out of view", ); - modify_reputation(ctx, origin, COST_UNEXPECTED_MESSAGE).await; + modify_reputation(ctx.sender(), origin, COST_UNEXPECTED_MESSAGE).await; return } @@ -926,7 +909,7 @@ async fn process_incoming_peer_message( ?relay_parent, "Advertise collation message has been received from an unknown peer", ); - modify_reputation(ctx, origin, COST_UNEXPECTED_MESSAGE).await; + modify_reputation(ctx.sender(), origin, COST_UNEXPECTED_MESSAGE).await; return }, Some(p) => p, @@ -962,7 +945,8 @@ async fn process_incoming_peer_message( collations.status = CollationStatus::Fetching; collations.waiting_collation = Some(id.clone()); - fetch_collation(ctx, state, pending_collation.clone(), id).await; + fetch_collation(ctx.sender(), state, pending_collation.clone(), id) + .await; }, CollationStatus::Seconded => { gum::trace!( @@ -984,7 +968,7 @@ async fn process_incoming_peer_message( "Invalid advertisement", ); - modify_reputation(ctx, origin, COST_UNEXPECTED_MESSAGE).await; + modify_reputation(ctx.sender(), origin, COST_UNEXPECTED_MESSAGE).await; }, } }, @@ -1011,16 +995,13 @@ async fn remove_relay_parent(state: &mut State, relay_parent: Hash) -> Result<() } /// Our view has changed. +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] async fn handle_our_view_change( ctx: &mut Context, state: &mut State, keystore: &SyncCryptoStorePtr, view: OurView, -) -> Result<()> -where - Context: overseer::SubsystemContext, - Context: SubsystemContext, -{ +) -> Result<()> { let old_view = std::mem::replace(&mut state.view, view); let added: HashMap> = state @@ -1061,7 +1042,7 @@ where ?para_id, "Disconnecting peer on view change (not current parachain id)" ); - disconnect_peer(ctx, peer_id.clone()).await; + disconnect_peer(ctx.sender(), peer_id.clone()).await; } } } @@ -1070,16 +1051,13 @@ where } /// Bridge event switch. +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] async fn handle_network_msg( ctx: &mut Context, state: &mut State, keystore: &SyncCryptoStorePtr, bridge_message: NetworkBridgeEvent, -) -> Result<()> -where - Context: overseer::SubsystemContext, - Context: SubsystemContext, -{ +) -> Result<()> { use NetworkBridgeEvent::*; match bridge_message { @@ -1109,15 +1087,13 @@ where } /// The main message receiver switch. +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] async fn process_msg( ctx: &mut Context, keystore: &SyncCryptoStorePtr, msg: CollatorProtocolMessage, state: &mut State, -) where - Context: overseer::SubsystemContext, - Context: SubsystemContext, -{ +) { use CollatorProtocolMessage::*; let _timer = state.metrics.time_process_msg(); @@ -1137,7 +1113,7 @@ async fn process_msg( ); }, ReportCollator(id) => { - report_collator(ctx, &state.peer_data, id).await; + report_collator(ctx.sender(), &state.peer_data, id).await; }, NetworkBridgeUpdate(event) => { if let Err(e) = handle_network_msg(ctx, state, keystore, event).await { @@ -1152,8 +1128,8 @@ async fn process_msg( if let Some(collation_event) = state.pending_candidates.remove(&parent) { let (collator_id, pending_collation) = collation_event; let PendingCollation { relay_parent, peer_id, .. } = pending_collation; - note_good_collation(ctx, &state.peer_data, collator_id).await; - notify_collation_seconded(ctx, peer_id, relay_parent, stmt).await; + note_good_collation(ctx.sender(), &state.peer_data, collator_id).await; + notify_collation_seconded(ctx.sender(), peer_id, relay_parent, stmt).await; if let Some(collations) = state.collations_per_relay_parent.get_mut(&parent) { collations.status = CollationStatus::Seconded; @@ -1184,7 +1160,7 @@ async fn process_msg( Entry::Vacant(_) => return, }; - report_collator(ctx, &state.peer_data, id.clone()).await; + report_collator(ctx.sender(), &state.peer_data, id.clone()).await; dequeue_next_collation_and_fetch(ctx, state, parent, id).await; }, @@ -1211,16 +1187,13 @@ fn infinite_stream(every: Duration) -> impl FusedStream { } /// The main run loop. +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] pub(crate) async fn run( mut ctx: Context, keystore: SyncCryptoStorePtr, eviction_policy: crate::CollatorEvictionPolicy, metrics: Metrics, -) -> std::result::Result<(), crate::error::FatalError> -where - Context: overseer::SubsystemContext, - Context: SubsystemContext, -{ +) -> std::result::Result<(), crate::error::FatalError> { let mut state = State { metrics, ..Default::default() }; let next_inactivity_stream = infinite_stream(ACTIVITY_POLL); @@ -1247,7 +1220,7 @@ where } } _ = next_inactivity_stream.next() => { - disconnect_inactive_peers(&mut ctx, &eviction_policy, &state.peer_data).await; + disconnect_inactive_peers(ctx.sender(), &eviction_policy, &state.peer_data).await; } res = state.collation_fetches.select_next_some() => { handle_collation_fetched_result(&mut ctx, &mut state, res).await; @@ -1270,7 +1243,7 @@ where ).await; for (peer_id, rep) in reputation_changes { - modify_reputation(&mut ctx, peer_id, rep).await; + modify_reputation(ctx.sender(), peer_id, rep).await; } }, } @@ -1304,9 +1277,9 @@ async fn poll_requests( } /// Dequeue another collation and fetch. -async fn dequeue_next_collation_and_fetch( - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] +async fn dequeue_next_collation_and_fetch( + ctx: &mut Context, state: &mut State, relay_parent: Hash, // The collator we tried to fetch from last. @@ -1323,7 +1296,7 @@ async fn dequeue_next_collation_and_fetch( ?id, "Successfully dequeued next advertisement - fetching ..." ); - fetch_collation(ctx, state, next, id).await; + fetch_collation(ctx.sender(), state, next, id).await; } else { gum::debug!( target: LOG_TARGET, @@ -1335,14 +1308,12 @@ async fn dequeue_next_collation_and_fetch( } /// Handle a fetched collation result. +#[overseer::contextbounds(CollatorProtocol, prefix = self::overseer)] async fn handle_collation_fetched_result( ctx: &mut Context, state: &mut State, (mut collation_event, res): PendingCollationFetch, -) where - Context: overseer::SubsystemContext, - Context: SubsystemContext, -{ +) { // If no prior collation for this relay parent has been seconded, then // memorize the `collation_event` for that `relay_parent`, such that we may // notify the collator of their successful second backing @@ -1380,12 +1351,13 @@ async fn handle_collation_fetched_result( if let Entry::Vacant(entry) = state.pending_candidates.entry(relay_parent) { collation_event.1.commitments_hash = Some(candidate_receipt.commitments_hash); - ctx.send_message(CandidateBackingMessage::Second( - relay_parent.clone(), - candidate_receipt, - pov, - )) - .await; + ctx.sender() + .send_message(CandidateBackingMessage::Second( + relay_parent.clone(), + candidate_receipt, + pov, + )) + .await; entry.insert(collation_event); } else { @@ -1401,18 +1373,15 @@ async fn handle_collation_fetched_result( // This issues `NetworkBridge` notifications to disconnect from all inactive peers at the // earliest possible point. This does not yet clean up any metadata, as that will be done upon // receipt of the `PeerDisconnected` event. -async fn disconnect_inactive_peers( - ctx: &mut Context, +async fn disconnect_inactive_peers( + sender: &mut impl overseer::CollatorProtocolSenderTrait, eviction_policy: &crate::CollatorEvictionPolicy, peers: &HashMap, -) where - Context: overseer::SubsystemContext, - Context: SubsystemContext, -{ +) { for (peer, peer_data) in peers { if peer_data.is_inactive(&eviction_policy) { gum::trace!(target: LOG_TARGET, "Disconnecting inactive peer"); - disconnect_peer(ctx, peer.clone()).await; + disconnect_peer(sender, peer.clone()).await; } } } diff --git a/node/network/dispute-distribution/src/lib.rs b/node/network/dispute-distribution/src/lib.rs index 8f45cbb11b45..3b195aa7b3e7 100644 --- a/node/network/dispute-distribution/src/lib.rs +++ b/node/network/dispute-distribution/src/lib.rs @@ -33,7 +33,7 @@ use polkadot_node_network_protocol::request_response::{incoming::IncomingRequest use polkadot_node_primitives::DISPUTE_WINDOW; use polkadot_node_subsystem::{ messages::DisputeDistributionMessage, overseer, FromOverseer, OverseerSignal, SpawnedSubsystem, - SubsystemContext, SubsystemError, + SubsystemError, }; use polkadot_node_subsystem_util::{runtime, runtime::RuntimeInfo}; @@ -114,12 +114,11 @@ pub struct DisputeDistributionSubsystem { metrics: Metrics, } -impl overseer::Subsystem for DisputeDistributionSubsystem +#[overseer::subsystem(DisputeDistribution, error = SubsystemError, prefix = self::overseer)] +impl DisputeDistributionSubsystem where - Context: SubsystemContext - + overseer::SubsystemContext - + Sync - + Send, + ::Sender: + overseer::DisputeDistributionSenderTrait + Sync + Send, AD: AuthorityDiscovery + Clone, { fn start(self, ctx: Context) -> SpawnedSubsystem { @@ -132,6 +131,7 @@ where } } +#[overseer::contextbounds(DisputeDistribution, prefix = self::overseer)] impl DisputeDistributionSubsystem where AD: AuthorityDiscovery + Clone, @@ -160,13 +160,7 @@ where } /// Start processing work as passed on from the Overseer. - async fn run(mut self, mut ctx: Context) -> std::result::Result<(), FatalError> - where - Context: SubsystemContext - + overseer::SubsystemContext - + Sync - + Send, - { + async fn run(mut self, mut ctx: Context) -> std::result::Result<(), FatalError> { let receiver = DisputesReceiver::new( ctx.sender().clone(), self.req_receiver @@ -205,7 +199,7 @@ where } /// Handle overseer signals. - async fn handle_signals( + async fn handle_signals( &mut self, ctx: &mut Context, signal: OverseerSignal, @@ -221,7 +215,7 @@ where } /// Handle `DisputeDistributionMessage`s. - async fn handle_subsystem_message( + async fn handle_subsystem_message( &mut self, ctx: &mut Context, msg: DisputeDistributionMessage, @@ -243,10 +237,10 @@ enum MuxedMessage { Sender(Option), } +#[overseer::contextbounds(DisputeDistribution, prefix = self::overseer)] impl MuxedMessage { - async fn receive( - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + async fn receive( + ctx: &mut Context, from_sender: &mut mpsc::Receiver, ) -> Self { // We are only fusing here to make `select` happy, in reality we will quit if the stream diff --git a/node/network/dispute-distribution/src/receiver/mod.rs b/node/network/dispute-distribution/src/receiver/mod.rs index c7a0a324e844..e061e67f4301 100644 --- a/node/network/dispute-distribution/src/receiver/mod.rs +++ b/node/network/dispute-distribution/src/receiver/mod.rs @@ -40,8 +40,8 @@ use polkadot_node_network_protocol::{ }; use polkadot_node_primitives::DISPUTE_WINDOW; use polkadot_node_subsystem::{ - messages::{AllMessages, DisputeCoordinatorMessage, ImportStatementsResult}, - SubsystemSender, + messages::{DisputeCoordinatorMessage, ImportStatementsResult}, + overseer, }; use polkadot_node_subsystem_util::{runtime, runtime::RuntimeInfo}; @@ -132,9 +132,10 @@ impl MuxedMessage { } } -impl DisputesReceiver +impl DisputesReceiver where AD: AuthorityDiscovery, + Sender: overseer::DisputeDistributionSenderTrait, { /// Create a new receiver which can be `run`. pub fn new( @@ -265,15 +266,13 @@ where let (pending_confirmation, confirmation_rx) = oneshot::channel(); let candidate_hash = candidate_receipt.hash(); self.sender - .send_message(AllMessages::DisputeCoordinator( - DisputeCoordinatorMessage::ImportStatements { - candidate_hash, - candidate_receipt, - session: valid_vote.0.session_index(), - statements: vec![valid_vote, invalid_vote], - pending_confirmation: Some(pending_confirmation), - }, - )) + .send_message(DisputeCoordinatorMessage::ImportStatements { + candidate_hash, + candidate_receipt, + session: valid_vote.0.session_index(), + statements: vec![valid_vote, invalid_vote], + pending_confirmation: Some(pending_confirmation), + }) .await; self.pending_imports.push(peer, confirmation_rx, pending_response); diff --git a/node/network/dispute-distribution/src/sender/mod.rs b/node/network/dispute-distribution/src/sender/mod.rs index b1a21b96ed2e..150e79eda108 100644 --- a/node/network/dispute-distribution/src/sender/mod.rs +++ b/node/network/dispute-distribution/src/sender/mod.rs @@ -20,10 +20,7 @@ use futures::channel::{mpsc, oneshot}; use polkadot_node_network_protocol::request_response::v1::DisputeRequest; use polkadot_node_primitives::{CandidateVotes, DisputeMessage, SignedDisputeStatement}; -use polkadot_node_subsystem::{ - messages::{AllMessages, DisputeCoordinatorMessage}, - ActiveLeavesUpdate, SubsystemContext, -}; +use polkadot_node_subsystem::{messages::DisputeCoordinatorMessage, overseer, ActiveLeavesUpdate}; use polkadot_node_subsystem_util::runtime::RuntimeInfo; use polkadot_primitives::v2::{CandidateHash, DisputeStatement, Hash, SessionIndex}; @@ -66,6 +63,7 @@ pub struct DisputeSender { metrics: Metrics, } +#[overseer::contextbounds(DisputeDistribution, prefix = self::overseer)] impl DisputeSender { /// Create a new `DisputeSender` which can be used to start dispute sendings. pub fn new(tx: mpsc::Sender, metrics: Metrics) -> Self { @@ -79,7 +77,7 @@ impl DisputeSender { } /// Create a `SendTask` for a particular new dispute. - pub async fn start_sender( + pub async fn start_sender( &mut self, ctx: &mut Context, runtime: &mut RuntimeInfo, @@ -114,7 +112,7 @@ impl DisputeSender { /// - Get new authorities to send messages to. /// - Get rid of obsolete tasks and disputes. /// - Get dispute sending started in case we missed one for some reason (e.g. on node startup) - pub async fn update_leaves( + pub async fn update_leaves( &mut self, ctx: &mut Context, runtime: &mut RuntimeInfo, @@ -179,7 +177,7 @@ impl DisputeSender { /// Call `start_sender` on all passed in disputes. /// /// Recover necessary votes for building up `DisputeMessage` and start sending for all of them. - async fn start_send_for_dispute( + async fn start_send_for_dispute( &mut self, ctx: &mut Context, runtime: &mut RuntimeInfo, @@ -303,7 +301,7 @@ impl DisputeSender { /// Make active sessions correspond to currently active heads. /// /// Returns: true if sessions changed. - async fn refresh_sessions( + async fn refresh_sessions( &mut self, ctx: &mut Context, runtime: &mut RuntimeInfo, @@ -321,7 +319,8 @@ impl DisputeSender { /// Retrieve the currently active sessions. /// /// List is all indices of all active sessions together with the head that was used for the query. -async fn get_active_session_indices( +#[overseer::contextbounds(DisputeDistribution, prefix = self::overseer)] +async fn get_active_session_indices( ctx: &mut Context, runtime: &mut RuntimeInfo, active_heads: &Vec, @@ -336,27 +335,29 @@ async fn get_active_session_indices( } /// Retrieve Set of active disputes from the dispute coordinator. -async fn get_active_disputes( +#[overseer::contextbounds(DisputeDistribution, prefix = self::overseer)] +async fn get_active_disputes( ctx: &mut Context, ) -> JfyiErrorResult> { let (tx, rx) = oneshot::channel(); + // Caller scope is in `update_leaves` and this is bounded by fork count. - ctx.send_unbounded_message(AllMessages::DisputeCoordinator( - DisputeCoordinatorMessage::ActiveDisputes(tx), - )); + ctx.send_unbounded_message(DisputeCoordinatorMessage::ActiveDisputes(tx)); rx.await.map_err(|_| JfyiError::AskActiveDisputesCanceled) } /// Get all locally available dispute votes for a given dispute. -async fn get_candidate_votes( +#[overseer::contextbounds(DisputeDistribution, prefix = self::overseer)] +async fn get_candidate_votes( ctx: &mut Context, session_index: SessionIndex, candidate_hash: CandidateHash, ) -> JfyiErrorResult> { let (tx, rx) = oneshot::channel(); // Caller scope is in `update_leaves` and this is bounded by fork count. - ctx.send_unbounded_message(AllMessages::DisputeCoordinator( - DisputeCoordinatorMessage::QueryCandidateVotes(vec![(session_index, candidate_hash)], tx), + ctx.send_unbounded_message(DisputeCoordinatorMessage::QueryCandidateVotes( + vec![(session_index, candidate_hash)], + tx, )); rx.await .map(|v| v.get(0).map(|inner| inner.to_owned().2)) diff --git a/node/network/dispute-distribution/src/sender/send_task.rs b/node/network/dispute-distribution/src/sender/send_task.rs index 9cf639e2e76b..03ce192697bb 100644 --- a/node/network/dispute-distribution/src/sender/send_task.rs +++ b/node/network/dispute-distribution/src/sender/send_task.rs @@ -26,10 +26,7 @@ use polkadot_node_network_protocol::{ }, IfDisconnected, }; -use polkadot_node_subsystem::{ - messages::{AllMessages, NetworkBridgeMessage}, - SubsystemContext, -}; +use polkadot_node_subsystem::{messages::NetworkBridgeMessage, overseer}; use polkadot_node_subsystem_util::{metrics, runtime::RuntimeInfo}; use polkadot_primitives::v2::{ AuthorityDiscoveryId, CandidateHash, Hash, SessionIndex, ValidatorIndex, @@ -100,9 +97,10 @@ impl TaskResult { } } +#[overseer::contextbounds(DisputeDistribution, prefix = self::overseer)] impl SendTask { /// Initiates sending a dispute message to peers. - pub async fn new( + pub async fn new( ctx: &mut Context, runtime: &mut RuntimeInfo, active_sessions: &HashMap, @@ -120,7 +118,7 @@ impl SendTask { /// /// This function is called at construction and should also be called whenever a session change /// happens and on a regular basis to ensure we are retrying failed attempts. - pub async fn refresh_sends( + pub async fn refresh_sends( &mut self, ctx: &mut Context, runtime: &mut RuntimeInfo, @@ -197,7 +195,8 @@ impl SendTask { /// /// This is all parachain validators of the session the candidate occurred and all authorities /// of all currently active sessions, determined by currently active heads. - async fn get_relevant_validators( + + async fn get_relevant_validators( &self, ctx: &mut Context, runtime: &mut RuntimeInfo, @@ -241,7 +240,8 @@ impl SendTask { /// Start sending of the given message to all given authorities. /// /// And spawn tasks for handling the response. -async fn send_requests( +#[overseer::contextbounds(DisputeDistribution, prefix = self::overseer)] +async fn send_requests( ctx: &mut Context, tx: mpsc::Sender, receivers: Vec, @@ -271,7 +271,7 @@ async fn send_requests( } let msg = NetworkBridgeMessage::SendRequests(reqs, IfDisconnected::ImmediateError); - ctx.send_message(AllMessages::NetworkBridge(msg)).await; + ctx.send_message(msg).await; Ok(statuses) } diff --git a/node/network/gossip-support/src/lib.rs b/node/network/gossip-support/src/lib.rs index f808cc7689dd..bd323b3fe96e 100644 --- a/node/network/gossip-support/src/lib.rs +++ b/node/network/gossip-support/src/lib.rs @@ -48,8 +48,7 @@ use polkadot_node_subsystem::{ GossipSupportMessage, NetworkBridgeEvent, NetworkBridgeMessage, RuntimeApiMessage, RuntimeApiRequest, }, - overseer, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemContext, - SubsystemError, + overseer, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemError, }; use polkadot_node_subsystem_util as util; use polkadot_primitives::v2::{ @@ -115,6 +114,7 @@ pub struct GossipSupport { metrics: Metrics, } +#[overseer::contextbounds(GossipSupport, prefix = self::overseer)] impl GossipSupport where AD: AuthorityDiscovery, @@ -138,11 +138,7 @@ where } } - async fn run(mut self, mut ctx: Context) -> Self - where - Context: SubsystemContext, - Context: overseer::SubsystemContext, - { + async fn run(mut self, mut ctx: Context) -> Self { fn get_connectivity_check_delay() -> Delay { Delay::new(LOW_CONNECTIVITY_WARN_DELAY) } @@ -178,7 +174,7 @@ where gum::trace!(target: LOG_TARGET, "active leaves signal"); let leaves = activated.into_iter().map(|a| a.hash); - if let Err(e) = self.handle_active_leaves(&mut ctx, leaves).await { + if let Err(e) = self.handle_active_leaves(ctx.sender(), leaves).await { gum::debug!(target: LOG_TARGET, error = ?e); } }, @@ -191,18 +187,13 @@ where /// 1. Determine if the current session index has changed. /// 2. If it has, determine relevant validators /// and issue a connection request. - async fn handle_active_leaves( + async fn handle_active_leaves( &mut self, - ctx: &mut Context, + sender: &mut impl overseer::GossipSupportSenderTrait, leaves: impl Iterator, - ) -> Result<(), util::Error> - where - Context: SubsystemContext, - Context: overseer::SubsystemContext, - { + ) -> Result<(), util::Error> { for leaf in leaves { - let current_index = - util::request_session_index_for_child(leaf, ctx.sender()).await.await??; + let current_index = util::request_session_index_for_child(leaf, sender).await.await??; let since_failure = self.last_failure.map(|i| i.elapsed()).unwrap_or_default(); let force_request = since_failure >= BACKOFF_DURATION; let leaf_session = Some((current_index, leaf)); @@ -216,7 +207,7 @@ where if let Some((session_index, relay_parent)) = maybe_issue_connection { let session_info = - util::request_session_info(leaf, session_index, ctx.sender()).await.await??; + util::request_session_info(leaf, session_index, sender).await.await??; let session_info = match session_info { Some(s) => s, @@ -255,13 +246,13 @@ where // by virtue of a new session being entered. Therefore we maintain // connections to a much broader set of validators. { - let mut connections = authorities_past_present_future(ctx, leaf).await?; + let mut connections = authorities_past_present_future(sender, leaf).await?; // Remove all of our locally controlled validator indices so we don't connect to ourself. // If we control none of them, don't issue connection requests - we're outside // of the 'clique' of recent validators. if remove_all_controlled(&self.keystore, &mut connections).await != 0 { - self.issue_connection_request(ctx, connections).await; + self.issue_connection_request(sender, connections).await; } } @@ -273,7 +264,7 @@ where self.update_authority_status_metrics(&session_info).await; update_gossip_topology( - ctx, + sender, our_index, session_info.discovery_keys, relay_parent, @@ -317,13 +308,12 @@ where } } - async fn issue_connection_request( + async fn issue_connection_request( &mut self, - ctx: &mut Context, + sender: &mut Sender, authorities: Vec, ) where - Context: SubsystemContext, - Context: overseer::SubsystemContext, + Sender: overseer::GossipSupportSenderTrait, { let num = authorities.len(); let mut validator_addrs = Vec::with_capacity(authorities.len()); @@ -347,11 +337,12 @@ where self.resolved_authorities = resolved; gum::debug!(target: LOG_TARGET, %num, "Issuing a connection request"); - ctx.send_message(NetworkBridgeMessage::ConnectToResolvedValidators { - validator_addrs, - peer_set: PeerSet::Validation, - }) - .await; + sender + .send_message(NetworkBridgeMessage::ConnectToResolvedValidators { + validator_addrs, + peer_set: PeerSet::Validation, + }) + .await; // issue another request for the same session // if at least a third of the authorities were not resolved. @@ -442,15 +433,11 @@ where } // Get the authorities of the past, present, and future. -async fn authorities_past_present_future( - ctx: &mut Context, +async fn authorities_past_present_future( + sender: &mut impl overseer::GossipSupportSenderTrait, relay_parent: Hash, -) -> Result, util::Error> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ - let authorities = util::request_authorities(relay_parent, ctx.sender()).await.await??; +) -> Result, util::Error> { + let authorities = util::request_authorities(relay_parent, sender).await.await??; gum::debug!( target: LOG_TARGET, authority_count = ?authorities.len(), @@ -500,28 +487,25 @@ async fn remove_all_controlled( /// This limits the amount of gossip peers to 2 * `sqrt(len)` and ensures the diameter of 2. /// /// [web3]: https://research.web3.foundation/en/latest/polkadot/networking/3-avail-valid.html#topology -async fn update_gossip_topology( - ctx: &mut Context, +async fn update_gossip_topology( + sender: &mut impl overseer::GossipSupportSenderTrait, our_index: usize, authorities: Vec, relay_parent: Hash, session_index: SessionIndex, -) -> Result<(), util::Error> -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +) -> Result<(), util::Error> { // retrieve BABE randomness let random_seed = { let (tx, rx) = oneshot::channel(); // TODO https://github.com/paritytech/polkadot/issues/5316: // get the random seed from the `SessionInfo` instead. - ctx.send_message(RuntimeApiMessage::Request( - relay_parent, - RuntimeApiRequest::CurrentBabeEpoch(tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + relay_parent, + RuntimeApiRequest::CurrentBabeEpoch(tx), + )) + .await; let randomness = rx.await??.randomness; let mut subject = [0u8; 40]; @@ -553,12 +537,13 @@ where .map(|i| (authorities[i].clone(), ValidatorIndex::from(i as u32))) .collect(); - ctx.send_message(NetworkBridgeMessage::NewGossipTopology { - session: session_index, - our_neighbors_x: row_neighbors, - our_neighbors_y: column_neighbors, - }) - .await; + sender + .send_message(NetworkBridgeMessage::NewGossipTopology { + session: session_index, + our_neighbors_x: row_neighbors, + our_neighbors_y: column_neighbors, + }) + .await; Ok(()) } @@ -596,10 +581,9 @@ fn matrix_neighbors( } } -impl overseer::Subsystem for GossipSupport +#[overseer::subsystem(GossipSupport, error = SubsystemError, prefix = self::overseer)] +impl GossipSupport where - Context: SubsystemContext, - Context: overseer::SubsystemContext, AD: AuthorityDiscovery + Clone, { fn start(self, ctx: Context) -> SpawnedSubsystem { diff --git a/node/network/statement-distribution/src/lib.rs b/node/network/statement-distribution/src/lib.rs index 4eae61f567b8..cfd19713a84a 100644 --- a/node/network/statement-distribution/src/lib.rs +++ b/node/network/statement-distribution/src/lib.rs @@ -38,11 +38,11 @@ use polkadot_node_subsystem_util::{self as util, rand, MIN_GOSSIP_PEERS}; use polkadot_node_subsystem::{ jaeger, messages::{ - AllMessages, CandidateBackingMessage, NetworkBridgeEvent, NetworkBridgeMessage, + CandidateBackingMessage, NetworkBridgeEvent, NetworkBridgeMessage, StatementDistributionMessage, }, overseer, ActiveLeavesUpdate, FromOverseer, OverseerSignal, PerLeafSpan, SpawnedSubsystem, - SubsystemContext, SubsystemError, + SubsystemError, }; use polkadot_primitives::v2::{ AuthorityDiscoveryId, CandidateHash, CommittedCandidateReceipt, CompactStatement, Hash, @@ -127,12 +127,8 @@ pub struct StatementDistributionSubsystem { rng: R, } -impl overseer::Subsystem - for StatementDistributionSubsystem -where - Context: SubsystemContext, - Context: overseer::SubsystemContext, -{ +#[overseer::subsystem(StatementDistribution, error=SubsystemError, prefix=self::overseer)] +impl StatementDistributionSubsystem { fn start(self, ctx: Context) -> SpawnedSubsystem { // Swallow error because failure is fatal to the node and we log with more precision // within `run`. @@ -630,10 +626,10 @@ enum MuxedMessage { Responder(Option), } +#[overseer::contextbounds(StatementDistribution, prefix = self::overseer)] impl MuxedMessage { - async fn receive( - ctx: &mut (impl SubsystemContext - + overseer::SubsystemContext), + async fn receive( + ctx: &mut Context, from_requester: &mut mpsc::Receiver, from_responder: &mut mpsc::Receiver, ) -> MuxedMessage { @@ -890,11 +886,12 @@ fn check_statement_signature( /// circulates the statement to all peers who have not seen it yet, and /// sends all statements dependent on that statement to peers who could previously not receive /// them but now can. -async fn circulate_statement_and_dependents( +#[overseer::contextbounds(StatementDistribution, prefix=self::overseer)] +async fn circulate_statement_and_dependents( gossip_peers: &HashSet, peers: &mut HashMap, active_heads: &mut HashMap, - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + ctx: &mut Context, relay_parent: Hash, statement: SignedFullStatement, priority_peers: Vec, @@ -1006,10 +1003,11 @@ fn is_statement_large(statement: &SignedFullStatement) -> (bool, Option) /// Circulates a statement to all peers who have not seen it yet, and returns /// an iterator over peers who need to have dependent statements sent. -async fn circulate_statement<'a>( +#[overseer::contextbounds(StatementDistribution, prefix=self::overseer)] +async fn circulate_statement<'a, Context>( gossip_peers: &HashSet, peers: &mut HashMap, - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + ctx: &mut Context, relay_parent: Hash, stored: StoredStatement<'a>, mut priority_peers: Vec, @@ -1081,10 +1079,10 @@ async fn circulate_statement<'a>( statement = ?stored.statement, "Sending statement", ); - ctx.send_message(AllMessages::NetworkBridge(NetworkBridgeMessage::SendValidationMessage( + ctx.send_message(NetworkBridgeMessage::SendValidationMessage( peers_to_send.iter().map(|(p, _)| p.clone()).collect(), payload, - ))) + )) .await; } @@ -1095,10 +1093,11 @@ async fn circulate_statement<'a>( } /// Send all statements about a given candidate hash to a peer. -async fn send_statements_about( +#[overseer::contextbounds(StatementDistribution, prefix=self::overseer)] +async fn send_statements_about( peer: PeerId, peer_data: &mut PeerData, - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + ctx: &mut Context, relay_parent: Hash, candidate_hash: CandidateHash, active_head: &ActiveHeadData, @@ -1120,21 +1119,19 @@ async fn send_statements_about( statement = ?statement.statement, "Sending statement", ); - ctx.send_message(AllMessages::NetworkBridge(NetworkBridgeMessage::SendValidationMessage( - vec![peer.clone()], - payload, - ))) - .await; + ctx.send_message(NetworkBridgeMessage::SendValidationMessage(vec![peer.clone()], payload)) + .await; metrics.on_statement_distributed(); } } /// Send all statements at a given relay-parent to a peer. -async fn send_statements( +#[overseer::contextbounds(StatementDistribution, prefix=self::overseer)] +async fn send_statements( peer: PeerId, peer_data: &mut PeerData, - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + ctx: &mut Context, relay_parent: Hash, active_head: &ActiveHeadData, metrics: &Metrics, @@ -1154,23 +1151,19 @@ async fn send_statements( statement = ?statement.statement, "Sending statement" ); - ctx.send_message(AllMessages::NetworkBridge(NetworkBridgeMessage::SendValidationMessage( - vec![peer.clone()], - payload, - ))) - .await; + ctx.send_message(NetworkBridgeMessage::SendValidationMessage(vec![peer.clone()], payload)) + .await; metrics.on_statement_distributed(); } } async fn report_peer( - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + sender: &mut impl overseer::StatementDistributionSenderTrait, peer: PeerId, rep: Rep, ) { - ctx.send_message(AllMessages::NetworkBridge(NetworkBridgeMessage::ReportPeer(peer, rep))) - .await + sender.send_message(NetworkBridgeMessage::ReportPeer(peer, rep)).await } /// If message contains a statement, then retrieve it, otherwise fork task to fetch it. @@ -1180,11 +1173,12 @@ async fn report_peer( /// your statement. /// /// If the message was large, but the result has been fetched already that one is returned. -async fn retrieve_statement_from_message<'a>( +#[overseer::contextbounds(StatementDistribution, prefix=self::overseer)] +async fn retrieve_statement_from_message<'a, Context>( peer: PeerId, message: protocol_v1::StatementDistributionMessage, active_head: &'a mut ActiveHeadData, - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + ctx: &mut Context, req_sender: &mpsc::Sender, metrics: &Metrics, ) -> Option { @@ -1272,11 +1266,12 @@ async fn retrieve_statement_from_message<'a>( /// Launch request for a large statement and get tracking status. /// /// Returns `None` if spawning task failed. -async fn launch_request( +#[overseer::contextbounds(StatementDistribution, prefix=self::overseer)] +async fn launch_request( meta: StatementMetadata, peer: PeerId, req_sender: mpsc::Sender, - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + ctx: &mut Context, metrics: &Metrics, ) -> Option { let (task, handle) = @@ -1302,19 +1297,21 @@ async fn launch_request( } /// Handle incoming message and circulate it to peers, if we did not know it already. -/// -async fn handle_incoming_message_and_circulate<'a>( +#[overseer::contextbounds(StatementDistribution, prefix=self::overseer)] +async fn handle_incoming_message_and_circulate<'a, Context, R>( peer: PeerId, gossip_peers: &HashSet, peers: &mut HashMap, active_heads: &'a mut HashMap, recent_outdated_heads: &RecentOutdatedHeads, - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + ctx: &mut Context, message: protocol_v1::StatementDistributionMessage, req_sender: &mpsc::Sender, metrics: &Metrics, - rng: &mut impl rand::Rng, -) { + rng: &mut R, +) where + R: rand::Rng, +{ let handled_incoming = match peers.get_mut(&peer) { Some(data) => handle_incoming_message( @@ -1360,12 +1357,13 @@ async fn handle_incoming_message_and_circulate<'a>( // // This function checks the signature and ensures the statement is compatible with our // view. It also notifies candidate backing if the statement was previously unknown. -async fn handle_incoming_message<'a>( +#[overseer::contextbounds(StatementDistribution, prefix=self::overseer)] +async fn handle_incoming_message<'a, Context>( peer: PeerId, peer_data: &mut PeerData, active_heads: &'a mut HashMap, recent_outdated_heads: &RecentOutdatedHeads, - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + ctx: &mut Context, message: protocol_v1::StatementDistributionMessage, req_sender: &mpsc::Sender, metrics: &Metrics, @@ -1383,7 +1381,7 @@ async fn handle_incoming_message<'a>( ); if !recent_outdated_heads.is_recent_outdated(&relay_parent) { - report_peer(ctx, peer, COST_UNEXPECTED_STATEMENT).await; + report_peer(ctx.sender(), peer, COST_UNEXPECTED_STATEMENT).await; } return None @@ -1393,7 +1391,7 @@ async fn handle_incoming_message<'a>( if let protocol_v1::StatementDistributionMessage::LargeStatement(_) = message { if let Err(rep) = peer_data.receive_large_statement(&relay_parent) { gum::debug!(target: LOG_TARGET, ?peer, ?message, ?rep, "Unexpected large statement.",); - report_peer(ctx, peer, rep).await; + report_peer(ctx.sender(), peer, rep).await; return None } } @@ -1434,16 +1432,16 @@ async fn handle_incoming_message<'a>( // Report peer merely if this is not a duplicate out-of-view statement that // was caused by a missing Seconded statement from this peer if unexpected_count == 0_usize { - report_peer(ctx, peer, rep).await; + report_peer(ctx.sender(), peer, rep).await; } }, // This happens when we have an unexpected remote peer that announced Seconded COST_UNEXPECTED_STATEMENT_REMOTE => { metrics.on_unexpected_statement_seconded(); - report_peer(ctx, peer, rep).await; + report_peer(ctx.sender(), peer, rep).await; }, _ => { - report_peer(ctx, peer, rep).await; + report_peer(ctx.sender(), peer, rep).await; }, } @@ -1464,7 +1462,7 @@ async fn handle_incoming_message<'a>( peer_data .receive(&relay_parent, &fingerprint, max_message_count) .expect("checked in `check_can_receive` above; qed"); - report_peer(ctx, peer, BENEFIT_VALID_STATEMENT).await; + report_peer(ctx.sender(), peer, BENEFIT_VALID_STATEMENT).await; return None }, @@ -1474,7 +1472,7 @@ async fn handle_incoming_message<'a>( match check_statement_signature(&active_head, relay_parent, unchecked_compact) { Err(statement) => { gum::debug!(target: LOG_TARGET, ?peer, ?statement, "Invalid statement signature"); - report_peer(ctx, peer, COST_INVALID_SIGNATURE).await; + report_peer(ctx.sender(), peer, COST_INVALID_SIGNATURE).await; return None }, Ok(statement) => statement, @@ -1500,7 +1498,7 @@ async fn handle_incoming_message<'a>( is_large_statement, "Full statement had bad payload." ); - report_peer(ctx, peer, COST_WRONG_HASH).await; + report_peer(ctx.sender(), peer, COST_WRONG_HASH).await; return None }, Ok(statement) => statement, @@ -1539,7 +1537,7 @@ async fn handle_incoming_message<'a>( unreachable!("checked in `is_useful_or_unknown` above; qed"); }, NotedStatement::Fresh(statement) => { - report_peer(ctx, peer, BENEFIT_VALID_STATEMENT_FIRST).await; + report_peer(ctx.sender(), peer, BENEFIT_VALID_STATEMENT_FIRST).await; let mut _span = handle_incoming_span.child("notify-backing"); @@ -1557,16 +1555,19 @@ async fn handle_incoming_message<'a>( } /// Update a peer's view. Sends all newly unlocked statements based on the previous -async fn update_peer_view_and_maybe_send_unlocked( +#[overseer::contextbounds(StatementDistribution, prefix=self::overseer)] +async fn update_peer_view_and_maybe_send_unlocked( peer: PeerId, gossip_peers: &HashSet, peer_data: &mut PeerData, - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + ctx: &mut Context, active_heads: &HashMap, new_view: View, metrics: &Metrics, - rng: &mut impl rand::Rng, -) { + rng: &mut R, +) where + R: rand::Rng, +{ let old_view = std::mem::replace(&mut peer_data.view, new_view); // Remove entries for all relay-parents in the old view but not the new. @@ -1596,18 +1597,21 @@ async fn update_peer_view_and_maybe_send_unlocked( } } -async fn handle_network_update( +#[overseer::contextbounds(StatementDistribution, prefix=self::overseer)] +async fn handle_network_update( peers: &mut HashMap, gossip_peers: &mut HashSet, authorities: &mut HashMap, active_heads: &mut HashMap, recent_outdated_heads: &RecentOutdatedHeads, - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + ctx: &mut Context, req_sender: &mpsc::Sender, update: NetworkBridgeEvent, metrics: &Metrics, - rng: &mut impl rand::Rng, -) { + rng: &mut R, +) where + R: rand::Rng, +{ match update { NetworkBridgeEvent::PeerConnected(peer, role, _, maybe_authority) => { gum::trace!(target: LOG_TARGET, ?peer, ?role, "Peer connected"); @@ -1701,6 +1705,7 @@ async fn handle_network_update( } } +#[overseer::contextbounds(StatementDistribution, prefix = self::overseer)] impl StatementDistributionSubsystem { /// Create a new Statement Distribution Subsystem pub fn new( @@ -1712,11 +1717,7 @@ impl StatementDistributionSubsystem { Self { keystore, req_receiver: Some(req_receiver), metrics, rng } } - async fn run( - mut self, - mut ctx: (impl SubsystemContext - + overseer::SubsystemContext), - ) -> std::result::Result<(), FatalError> { + async fn run(mut self, mut ctx: Context) -> std::result::Result<(), FatalError> { let mut peers: HashMap = HashMap::new(); let mut gossip_peers: HashSet = HashSet::new(); let mut authorities: HashMap = HashMap::new(); @@ -1832,9 +1833,9 @@ impl StatementDistributionSubsystem { Ok(()) } - async fn handle_requester_message( + async fn handle_requester_message( &mut self, - ctx: &mut impl SubsystemContext, + ctx: &mut Context, gossip_peers: &HashSet, peers: &mut HashMap, active_heads: &mut HashMap, @@ -1851,9 +1852,9 @@ impl StatementDistributionSubsystem { bad_peers, } => { for bad in bad_peers { - report_peer(ctx, bad, COST_FETCH_FAIL).await; + report_peer(ctx.sender(), bad, COST_FETCH_FAIL).await; } - report_peer(ctx, from_peer, BENEFIT_VALID_RESPONSE).await; + report_peer(ctx.sender(), from_peer, BENEFIT_VALID_RESPONSE).await; let active_head = active_heads .get_mut(&relay_parent) @@ -1898,10 +1899,10 @@ impl StatementDistributionSubsystem { } }, RequesterMessage::SendRequest(req) => { - ctx.send_message(AllMessages::NetworkBridge(NetworkBridgeMessage::SendRequests( + ctx.send_message(NetworkBridgeMessage::SendRequests( vec![req], IfDisconnected::ImmediateError, - ))) + )) .await; }, RequesterMessage::GetMorePeers { relay_parent, candidate_hash, tx } => { @@ -1935,14 +1936,14 @@ impl StatementDistributionSubsystem { } } }, - RequesterMessage::ReportPeer(peer, rep) => report_peer(ctx, peer, rep).await, + RequesterMessage::ReportPeer(peer, rep) => report_peer(ctx.sender(), peer, rep).await, } Ok(()) } - async fn handle_subsystem_message( + async fn handle_subsystem_message( &mut self, - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + ctx: &mut Context, runtime: &mut RuntimeInfo, peers: &mut HashMap, gossip_peers: &mut HashSet, diff --git a/node/network/statement-distribution/src/tests.rs b/node/network/statement-distribution/src/tests.rs index 49a8e5cd1426..a0342fea2eba 100644 --- a/node/network/statement-distribution/src/tests.rs +++ b/node/network/statement-distribution/src/tests.rs @@ -29,7 +29,7 @@ use polkadot_node_network_protocol::{ use polkadot_node_primitives::{Statement, UncheckedSignedFullStatement}; use polkadot_node_subsystem::{ jaeger, - messages::{network_bridge_event, RuntimeApiMessage, RuntimeApiRequest}, + messages::{network_bridge_event, AllMessages, RuntimeApiMessage, RuntimeApiRequest}, ActivatedLeaf, LeafStatus, }; use polkadot_node_subsystem_test_helpers::mock::make_ferdie_keystore; diff --git a/node/overseer/examples/minimal-example.rs b/node/overseer/examples/minimal-example.rs index fc07672ef4a1..c3cbbd3b289a 100644 --- a/node/overseer/examples/minimal-example.rs +++ b/node/overseer/examples/minimal-example.rs @@ -24,14 +24,12 @@ use std::time::Duration; use ::test_helpers::{dummy_candidate_descriptor, dummy_hash}; use polkadot_node_primitives::{BlockData, PoV}; -use polkadot_node_subsystem_types::messages::{ - CandidateBackingMessage, CandidateValidationMessage, -}; +use polkadot_node_subsystem_types::messages::CandidateValidationMessage; use polkadot_overseer::{ self as overseer, dummy::dummy_overseer_builder, gen::{FromOverseer, SpawnedSubsystem}, - AllMessages, HeadSupportsParachains, OverseerSignal, SubsystemError, + HeadSupportsParachains, SubsystemError, }; use polkadot_primitives::v2::{CandidateReceipt, Hash}; @@ -46,15 +44,9 @@ impl HeadSupportsParachains for AlwaysSupportsParachains { struct Subsystem1; +#[overseer::contextbounds(CandidateBacking, prefix = self::overseer)] impl Subsystem1 { - async fn run(mut ctx: Ctx) -> () - where - Ctx: overseer::SubsystemContext< - Message = CandidateBackingMessage, - AllMessages = AllMessages, - Signal = OverseerSignal, - >, - { + async fn run(mut ctx: Context) { 'louy: loop { match ctx.try_recv().await { Ok(Some(msg)) => { @@ -84,21 +76,14 @@ impl Subsystem1 { Default::default(), tx, ); - ctx.send_message(::AllMessages::from(msg)) - .await; + ctx.send_message(msg).await; } () } } -impl overseer::Subsystem for Subsystem1 -where - Context: overseer::SubsystemContext< - Message = CandidateBackingMessage, - AllMessages = AllMessages, - Signal = OverseerSignal, - >, -{ +#[overseer::subsystem(CandidateBacking, error = SubsystemError, prefix = self::overseer)] +impl Subsystem1 { fn start(self, ctx: Context) -> SpawnedSubsystem { let future = Box::pin(async move { Self::run(ctx).await; @@ -113,15 +98,9 @@ where struct Subsystem2; +#[overseer::contextbounds(CandidateValidation, prefix = self::overseer)] impl Subsystem2 { - async fn run(mut ctx: Ctx) - where - Ctx: overseer::SubsystemContext< - Message = CandidateValidationMessage, - AllMessages = AllMessages, - Signal = OverseerSignal, - >, - { + async fn run(mut ctx: Context) -> () { ctx.spawn( "subsystem-2-job", Box::pin(async { @@ -151,14 +130,8 @@ impl Subsystem2 { } } -impl overseer::Subsystem for Subsystem2 -where - Context: overseer::SubsystemContext< - Message = CandidateValidationMessage, - AllMessages = AllMessages, - Signal = OverseerSignal, - >, -{ +#[overseer::subsystem(CandidateValidation, error = SubsystemError, prefix = self::overseer)] +impl Subsystem2 { fn start(self, ctx: Context) -> SpawnedSubsystem { let future = Box::pin(async move { Self::run(ctx).await; @@ -181,6 +154,7 @@ fn main() { .unwrap() .replace_candidate_validation(|_| Subsystem2) .replace_candidate_backing(|orig| orig) + .replace_candidate_backing(|_orig| Subsystem1) .build() .unwrap(); diff --git a/node/overseer/overseer-gen/Cargo.toml b/node/overseer/overseer-gen/Cargo.toml index 8a8d1a61ca20..e8c2b569a023 100644 --- a/node/overseer/overseer-gen/Cargo.toml +++ b/node/overseer/overseer-gen/Cargo.toml @@ -4,6 +4,7 @@ version = "0.9.19" authors = ["Parity Technologies "] edition = "2021" description = "Generate an overseer including builder pattern and message wrapper from a single struct." +autoexamples = false [dependencies] gum = { package = "tracing-gum", path = "../../gum" } @@ -22,6 +23,16 @@ pin-project = "1.0" trybuild = "1.0.61" rustversion = "1.0.6" + + +[[example]] +name = "duo" +crate-type = ["bin"] + +[[example]] +name = "solo" +crate-type = ["bin"] + [features] default = [] expand = ["polkadot-overseer-gen-proc-macro/expand"] diff --git a/node/overseer/overseer-gen/README.md b/node/overseer/overseer-gen/README.md index cd1098a7a16c..ebce568ce9a5 100644 --- a/node/overseer/overseer-gen/README.md +++ b/node/overseer/overseer-gen/README.md @@ -11,17 +11,20 @@ declarative. ```rust #[overlord(signal=SigSigSig, event=Event, gen=AllMessages, error=OverseerError)] pub struct Overseer { - #[subsystem(MsgA)] + #[subsystem(MsgA, sends: [MsgB])] sub_a: AwesomeSubSysA, - #[subsystem(MsgB)] + #[subsystem(MsgB, sends: [MsgA])] sub_b: AwesomeSubSysB, } ``` * Each subsystem is annotated with `#[subsystem(_)]` where `MsgA` respectively `MsgB` are the messages being consumed by that particular subsystem. Each of those subsystems is required to implement the subsystem -trait. +trait with the correct trait bounds. Commonly this is achieved +by using `#[subsystem]` and `#[contextbounds]` macro. + * `#[contextbounds(Foo, error=Yikes, prefix=wherethetraitsat)]` can applied to `impl`-blocks and `fn`-blocks. It will add additional trait bounds for the generic `Context` with `Context: FooContextTrait` for `::Sender: FooSenderTrait` besides a few more. Note that `Foo` here references the name of the subsystem as declared in `#[overlord(..)]` macro. + * `#[subsystem(Foo, error=Yikes, prefix=wherethetraitsat)]` is a extension to the above, implementing `trait Subsystem`. * `error=` tells the overseer to use the user provided error type, if not provided a builtin one is used. Note that this is the one error type used throughout all calls, so make sure it does impl `From` for all other error types `E` that are relevant to your application. * `event=` declares an external event type, that injects certain events @@ -63,10 +66,10 @@ is not ready to be included in the Overseer: ```rust #[overlord(signal=SigSigSig, event=Event, gen=AllMessages, error=OverseerError)] pub struct Overseer { - #[subsystem(MsgA)] + #[subsystem(MsgA, sends: MsgB)] sub_a: AwesomeSubSysA, - #[subsystem(MsgB), wip] + #[subsystem(MsgB, sends: MsgA), wip] sub_b: AwesomeSubSysB, // This subsystem will not be required nor allowed to be set } ``` diff --git a/node/overseer/overseer-gen/adr/01-adr.md b/node/overseer/overseer-gen/adr/01-adr.md new file mode 100644 index 000000000000..410f5a699b29 --- /dev/null +++ b/node/overseer/overseer-gen/adr/01-adr.md @@ -0,0 +1,21 @@ +# Limit outgoing messages + +## Status + +Accepted + implemented. + +## Context + +Previously, there was no way to limit and hence reason about a subset of subsystems, and if they form a cycle. Limiting the outgoing message types is a first step to create respective graphs and use classic graph algorithms to detect those and leave it to the user to resolve these. + +## Decision + +Annotate the `#[overlord]` inner `#[subsystem(..)]` annotation +with an aditional set of outgoing messages and enforce this via more fine grained trait bounds on the `Sender` and `::Sender` bounds. + +## Consequences + +* A graph will be spawn for every compilation under the `OUT_DIR` of the crate where `#[overlord]` is specified. +* Each subsystem has a consuming message which is often referred to as generic `M` (no change on that, is as before), but now we have trait `AssociateOutgoing { type OutgoingMessages = ..; }` which defines an outgoing helper `enum` that is generated with an ident constructed as `${Subsystem}OutgoingMessages` where `${Subsystem}` is the subsystem identifier as used in the overseer declaration. `${Subsystem}OutgoingMessages` is used throughout everywhere to constrain the outgoing messages (commonly referred to as `OutgoingMessage` generic bounded by `${Subsystem}OutgoingMessages: From` or `::OutgoingMessages: From`. It's what allows the construction of the graph and compile time verification. +* `${Subsystem}SenderTrait` and `${Subsystem}ContextTrait` are accumulation traits or wrapper traits, that combine over all annotated M or `OutgoingMessages` from the overseer declaration or their respective outgoing types. It is usage convenience and assures consistency within a subsystem while also maintaining a single source of truth for which messages can be sent by a particular subsystem. Note that this is sidestepped for the test subsystem, which may consume `gen=AllMessages`, the global message wrapper type. +* `Job`-based subsystems, being on their way out, are patched, but they now are generic over the `Sender` type, leaking that type. diff --git a/node/overseer/overseer-gen/examples/dummy.rs b/node/overseer/overseer-gen/examples/dummy.rs deleted file mode 100644 index d263d9fca887..000000000000 --- a/node/overseer/overseer-gen/examples/dummy.rs +++ /dev/null @@ -1,143 +0,0 @@ -//! A dummy to be used with cargo expand - -use polkadot_node_network_protocol::WrongVariant; -use polkadot_overseer_gen::*; -use std::collections::HashMap; - -/// Concrete subsystem implementation for `MsgStrukt` msg type. -#[derive(Default)] -pub struct AwesomeSubSys; - -impl ::polkadot_overseer_gen::Subsystem, Yikes> for AwesomeSubSys { - fn start(self, _ctx: XxxSubsystemContext) -> SpawnedSubsystem { - unimplemented!("starting yay!") - } -} - -#[derive(Default)] -pub struct GoblinTower; - -impl ::polkadot_overseer_gen::Subsystem, Yikes> for GoblinTower { - fn start(self, _ctx: XxxSubsystemContext) -> SpawnedSubsystem { - unimplemented!("welcum") - } -} - -/// A signal sent by the overseer. -#[derive(Debug, Clone)] -pub struct SigSigSig; - -/// The external event. -#[derive(Debug, Clone)] -pub struct EvX; - -impl EvX { - pub fn focus<'a, T>(&'a self) -> Result { - unimplemented!("dispatch") - } -} - -#[derive(Debug, Clone, Copy)] -pub struct Yikes; - -impl std::fmt::Display for Yikes { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "yikes!") - } -} - -impl std::error::Error for Yikes {} - -impl From for Yikes { - fn from(_: polkadot_overseer_gen::OverseerError) -> Yikes { - Yikes - } -} - -impl From for Yikes { - fn from(_: polkadot_overseer_gen::mpsc::SendError) -> Yikes { - Yikes - } -} - -#[derive(Debug, Clone)] -pub struct MsgStrukt(u8); - -#[derive(Debug, Clone, Copy)] -pub struct Plinko; - -impl From for MsgStrukt { - fn from(_event: NetworkMsg) -> Self { - MsgStrukt(1u8) - } -} - -#[derive(Debug, Clone, Copy)] -pub enum NetworkMsg { - A, - B, - C, -} - -impl NetworkMsg { - fn focus(&self) -> Result { - Ok(match self { - Self::B => return Err(WrongVariant), - Self::A | Self::C => self.clone(), - }) - } -} - -#[overlord(signal=SigSigSig, event=EvX, error=Yikes, network=NetworkMsg, gen=AllMessages)] -struct Xxx { - #[subsystem(MsgStrukt)] - sub0: AwesomeSubSys, - - #[subsystem(no_dispatch, blocking, Plinko)] - plinkos: GoblinTower, - - i_like_pi: f64, - i_like_generic: T, - i_like_hash: HashMap, -} - -#[derive(Debug, Clone)] -struct DummySpawner; - -impl SpawnNamed for DummySpawner { - fn spawn_blocking( - &self, - task_name: &'static str, - subsystem_name: Option<&'static str>, - _future: futures::future::BoxFuture<'static, ()>, - ) { - unimplemented!("spawn blocking {} {}", task_name, subsystem_name.unwrap_or("default")) - } - - fn spawn( - &self, - task_name: &'static str, - subsystem_name: Option<&'static str>, - _future: futures::future::BoxFuture<'static, ()>, - ) { - unimplemented!("spawn {} {}", task_name, subsystem_name.unwrap_or("default")) - } -} - -#[derive(Debug, Clone)] -struct DummyCtx; - -fn main() { - let (overseer, _handle): (Xxx<_, f64>, _) = Xxx::builder() - .sub0(AwesomeSubSys::default()) - .plinkos(GoblinTower::default()) - .i_like_pi(::std::f64::consts::PI) - .i_like_generic(42.0) - .i_like_hash(HashMap::new()) - .spawner(DummySpawner) - .build() - .unwrap(); - assert_eq!(overseer.i_like_pi.floor() as i8, 3); - assert_eq!(overseer.i_like_generic.floor() as i8, 42); - assert_eq!(overseer.i_like_hash.len() as i8, 0); -} diff --git a/node/overseer/overseer-gen/examples/duo.rs b/node/overseer/overseer-gen/examples/duo.rs new file mode 100644 index 000000000000..b63a7df603e1 --- /dev/null +++ b/node/overseer/overseer-gen/examples/duo.rs @@ -0,0 +1,89 @@ +#![allow(dead_code)] // overseer events are not used + +//! A dummy to be used with cargo expand + +use polkadot_overseer_gen::{self as overseer, SpawnNamed, *}; +use std::collections::HashMap; +mod misc; + +pub use self::misc::*; + +/// Concrete subsystem implementation for `MsgStrukt` msg type. +#[derive(Default)] +pub struct AwesomeSubSys; + +#[overseer::subsystem(Awesome, error=Yikes)] +impl AwesomeSubSys { + fn start(self, mut ctx: Context) -> SpawnedSubsystem { + let mut sender = ctx.sender().clone(); + ctx.spawn( + "AwesomeSubsys", + Box::pin(async move { + sender.send_message(Plinko).await; + }), + ) + .unwrap(); + unimplemented!("starting yay!") + } +} + +#[derive(Default)] +pub struct Fortified; + +#[overseer::subsystem(GoblinTower, error=Yikes)] +impl Fortified { + fn start(self, mut ctx: Context) -> SpawnedSubsystem { + let mut sender = ctx.sender().clone(); + ctx.spawn( + "GoblinTower", + Box::pin(async move { + sender.send_message(MsgStrukt(8u8)).await; + }), + ) + .unwrap(); + unimplemented!("welcum") + } +} + +#[overlord(signal=SigSigSig, event=EvX, error=Yikes, gen=AllMessages)] +struct Duo { + #[subsystem(consumes: MsgStrukt, sends: [Plinko])] + sub0: Awesome, + + #[subsystem(blocking, consumes: Plinko, sends: [MsgStrukt])] + plinkos: GoblinTower, + + i_like_pi: f64, + i_like_generic: T, + i_like_hash: HashMap, +} + +fn main() { + use futures::{executor, pin_mut}; + + executor::block_on(async move { + let (overseer, _handle): (Duo<_, f64>, _) = Duo::builder() + .sub0(AwesomeSubSys::default()) + .plinkos(Fortified::default()) + .i_like_pi(::std::f64::consts::PI) + .i_like_generic(42.0) + .i_like_hash(HashMap::new()) + .spawner(DummySpawner) + .build() + .unwrap(); + + assert_eq!(overseer.i_like_pi.floor() as i8, 3); + assert_eq!(overseer.i_like_generic.floor() as i8, 42); + assert_eq!(overseer.i_like_hash.len() as i8, 0); + + let overseer_fut = overseer + .running_subsystems + .into_future() + .timeout(std::time::Duration::from_millis(300)) + .fuse(); + + pin_mut!(overseer_fut); + + overseer_fut.await + }); +} diff --git a/node/overseer/overseer-gen/examples/misc.rs b/node/overseer/overseer-gen/examples/misc.rs new file mode 100644 index 000000000000..87f674e14134 --- /dev/null +++ b/node/overseer/overseer-gen/examples/misc.rs @@ -0,0 +1,69 @@ +use polkadot_overseer_gen::{SpawnNamed, *}; + +#[derive(Debug, Clone, Copy)] +pub enum SigSigSig { + Conclude, + Foo, +} + +#[derive(Debug, Clone)] +pub struct DummySpawner; + +impl SpawnNamed for DummySpawner { + fn spawn_blocking( + &self, + task_name: &'static str, + subsystem_name: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + unimplemented!("spawn blocking {} {}", task_name, subsystem_name.unwrap_or("default")) + } + + fn spawn( + &self, + task_name: &'static str, + subsystem_name: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + unimplemented!("spawn {} {}", task_name, subsystem_name.unwrap_or("default")) + } +} + +/// The external event. +#[derive(Debug, Clone)] +pub struct EvX; + +impl EvX { + pub fn focus<'a, T>(&'a self) -> Result { + unimplemented!("focus") + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Yikes; + +impl std::fmt::Display for Yikes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "yikes!") + } +} + +impl std::error::Error for Yikes {} + +impl From for Yikes { + fn from(_: polkadot_overseer_gen::OverseerError) -> Yikes { + Yikes + } +} + +impl From for Yikes { + fn from(_: polkadot_overseer_gen::mpsc::SendError) -> Yikes { + Yikes + } +} + +#[derive(Debug, Clone)] +pub struct MsgStrukt(pub u8); + +#[derive(Debug, Clone, Copy)] +pub struct Plinko; diff --git a/node/overseer/overseer-gen/examples/solo.rs b/node/overseer/overseer-gen/examples/solo.rs new file mode 100644 index 000000000000..74ecdc9d16ba --- /dev/null +++ b/node/overseer/overseer-gen/examples/solo.rs @@ -0,0 +1,54 @@ +#![allow(dead_code)] // overseer events are not used + +//! A minimal demo to be used with cargo expand. + +use polkadot_overseer_gen::{self as overseer, SpawnNamed, *}; +mod misc; + +pub use self::misc::*; + +#[overlord(signal=SigSigSig, event=EvX, error=Yikes, gen=AllMessages)] +struct Solo { + #[subsystem(consumes: Plinko, sends: [MsgStrukt])] + goblin_tower: GoblinTower, +} + +#[derive(Default)] +pub struct Fortified; + +#[overseer::subsystem(GoblinTower, error=Yikes)] +impl Fortified { + fn start(self, mut ctx: Context) -> SpawnedSubsystem { + let mut sender = ctx.sender().clone(); + ctx.spawn( + "GoblinTower", + Box::pin(async move { + sender.send_message(MsgStrukt(8u8)).await; + }), + ) + .unwrap(); + unimplemented!("welcum") + } +} + +fn main() { + use futures::{executor, pin_mut}; + + executor::block_on(async move { + let (overseer, _handle): (Solo<_>, _) = Solo::builder() + .goblin_tower(Fortified::default()) + .spawner(DummySpawner) + .build() + .unwrap(); + + let overseer_fut = overseer + .running_subsystems + .into_future() + .timeout(std::time::Duration::from_millis(300)) + .fuse(); + + pin_mut!(overseer_fut); + + overseer_fut.await + }); +} diff --git a/node/overseer/overseer-gen/proc-macro/Cargo.toml b/node/overseer/overseer-gen/proc-macro/Cargo.toml index 436384ca0acb..7c6dcbd1b4ee 100644 --- a/node/overseer/overseer-gen/proc-macro/Cargo.toml +++ b/node/overseer/overseer-gen/proc-macro/Cargo.toml @@ -17,12 +17,19 @@ quote = "1.0.18" proc-macro2 = "1.0.37" proc-macro-crate = "1.1.3" expander = { version = "0.0.6", default-features = false } +petgraph = "0.6.0" [dev-dependencies] assert_matches = "1.5.0" +polkadot-overseer-gen = { path = "../" } +thiserror = "1" +gum = { package = "tracing-gum", path = "../../../gum" } [features] -default = [] +default = ["graph", "expand"] # write the expanded version to a `overlord-expansion.[a-f0-9]{10}.rs` # in the `OUT_DIR` as defined by `cargo` for the `expander` crate. expand = [] +# Create directional message consuming / outgoing graph. +# Generates: `${OUT_DIR}/${overseer|lowercase}-subsystem-messaging.dot` +graph = [] diff --git a/node/overseer/overseer-gen/proc-macro/build.rs b/node/overseer/overseer-gen/proc-macro/build.rs new file mode 100644 index 000000000000..24bd09048397 --- /dev/null +++ b/node/overseer/overseer-gen/proc-macro/build.rs @@ -0,0 +1,3 @@ +fn main() { + // populate OUT_DIR +} diff --git a/node/overseer/overseer-gen/proc-macro/src/impl_builder.rs b/node/overseer/overseer-gen/proc-macro/src/impl_builder.rs index 6d634cc9f344..ceb9faf0ca39 100644 --- a/node/overseer/overseer-gen/proc-macro/src/impl_builder.rs +++ b/node/overseer/overseer-gen/proc-macro/src/impl_builder.rs @@ -103,7 +103,7 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream { info.subsystems().iter().filter(|ssf| !ssf.wip).enumerate().map(|(idx, ssf)| { let field_name = &ssf.name; let field_type = &ssf.generic; - let subsystem_consumes = &ssf.consumes; + let subsystem_consumes = &ssf.message_to_consume; // Remove state generic for the item to be replaced. It sufficient to know `field_type` for // that since we always move from `Init<#field_type>` to `Init`. let impl_subsystem_state_generics = recollect_without_idx(&subsystem_passthrough_state_generics[..], idx); @@ -130,19 +130,28 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream { // see the loop below. let to_keep_subsystem_name = recollect_without_idx(&subsystem_name[..], idx); + let subsystem_sender_trait = format_ident!("{}SenderTrait", field_type); + let _subsystem_ctx_trait = format_ident!("{}ContextTrait", field_type); + + let builder_where_clause = quote!{ + #field_type : #support_crate::Subsystem< #subsystem_ctx_name< #subsystem_consumes >, #error_ty>, + < #subsystem_ctx_name < #subsystem_consumes > as #support_crate :: SubsystemContext>::Sender: + #subsystem_sender_trait, + }; + // Create the field init `fn` quote! { impl #builder where - #field_type : Subsystem<#subsystem_ctx_name<#subsystem_consumes>, #error_ty>, + #builder_where_clause { /// Specify the subsystem in the builder directly pub fn #field_name (self, var: #field_type ) -> #builder { #builder { - #field_name: Init::<#field_type>::Value(var), + #field_name: Init::< #field_type >::Value(var), #( #to_keep_subsystem_name: self. #to_keep_subsystem_name, )* @@ -158,7 +167,7 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream { /// Specify the the initialization function for a subsystem pub fn #field_name_with<'a, F>(self, subsystem_init_fn: F ) -> #builder - where + where F: 'static + FnOnce(#handle) -> ::std::result::Result<#field_type, #error_ty>, { @@ -185,7 +194,7 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream { impl #builder where - #field_type : Subsystem<#subsystem_ctx_name<#subsystem_consumes>, #error_ty>, + #builder_where_clause { /// Replace a subsystem by another implementation for the /// consumable message type. @@ -301,6 +310,28 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream { S, #( #baggage_generic_ty, )* #( #subsystem_generics, )* }; + let builder_where_clause = info + .subsystems() + .iter() + .map(|ssf| { + let field_type = &ssf.generic; + let consumes = &ssf.message_to_consume; + let subsystem_sender_trait = format_ident!("{}SenderTrait", ssf.generic); + let subsystem_ctx_trait = format_ident!("{}ContextTrait", ssf.generic); + quote! { + #field_type: + #support_crate::Subsystem< #subsystem_ctx_name < #consumes>, #error_ty>, + <#subsystem_ctx_name< #consumes > as #subsystem_ctx_trait>::Sender: + #subsystem_sender_trait, + #subsystem_ctx_name< #consumes >: + #subsystem_ctx_trait, + } + }) + .fold(TokenStream::new(), |mut ts, addendum| { + ts.extend(addendum); + ts + }); + let mut ts = quote! { /// Convenience alias. type SubsystemInitFn = Box ::std::result::Result >; @@ -332,14 +363,15 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream { } } - impl #overseer_name where #spawner_where_clause { + impl #overseer_name + where + #spawner_where_clause, + { /// Create a new overseer utilizing the builder. pub fn builder< #( #subsystem_generics),* >() -> - #builder #(, Missing<#field_type> )* > + #builder #(, Missing< #field_type > )* > where - #( - #subsystem_generics : Subsystem<#subsystem_ctx_name< #consumes >, #error_ty>, - )* + #builder_where_clause { #builder :: new() } @@ -398,7 +430,8 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream { ts.extend(quote!{ /// Builder pattern to create compile time safe construction path. - pub struct #builder { + pub struct #builder + { #( #subsystem_name: #subsystem_passthrough_state_generics, )* @@ -445,7 +478,7 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream { impl #builder, #( #subsystem_passthrough_state_generics, )* #( #baggage_passthrough_state_generics, )*> where - #spawner_where_clause + #spawner_where_clause, { /// The `spawner` to use for spawning tasks. pub fn spawner(self, spawner: S) -> #builder< @@ -490,6 +523,12 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream { } }); + // Create the string literals for spawn. + let subsystem_name_str_literal = subsystem_name + .iter() + .map(|ident| proc_macro2::Literal::string(ident.to_string().replace("_", "-").as_str())) + .collect::>(); + ts.extend(quote! { /// Type used to represent a builder where all fields are initialized and the overseer could be constructed. pub type #initialized_builder<#initialized_builder_generics> = #builder, #( Init<#field_type>, )*>; @@ -498,9 +537,7 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream { impl<#initialized_builder_generics> #initialized_builder<#initialized_builder_generics> where #spawner_where_clause, - #( - #subsystem_generics : Subsystem<#subsystem_ctx_name< #consumes >, #error_ty>, - )* + #builder_where_clause { /// Complete the construction and create the overseer type. pub fn build(self) @@ -577,17 +614,12 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream { self.signal_capacity.unwrap_or(SIGNAL_CHANNEL_CAPACITY) ); - // Generate subsystem name based on overseer field name. - let subsystem_string = String::from(stringify!(#subsystem_name)); - // Convert owned `snake case` string to a `kebab case` static str. - let subsystem_static_str = Box::leak(subsystem_string.replace("_", "-").into_boxed_str()); - let ctx = #subsystem_ctx_name::< #consumes >::new( signal_rx, message_rx, channels_out.clone(), to_overseer_tx.clone(), - subsystem_static_str + #subsystem_name_str_literal ); let #subsystem_name: OverseenSubsystem< #consumes > = @@ -598,7 +630,7 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream { unbounded_meter, ctx, #subsystem_name, - subsystem_static_str, + #subsystem_name_str_literal, &mut running_subsystems, )?; )* diff --git a/node/overseer/overseer-gen/proc-macro/src/impl_channels_out.rs b/node/overseer/overseer-gen/proc-macro/src/impl_channels_out.rs index f61071abc8e1..a337a69d46a9 100644 --- a/node/overseer/overseer-gen/proc-macro/src/impl_channels_out.rs +++ b/node/overseer/overseer-gen/proc-macro/src/impl_channels_out.rs @@ -65,6 +65,7 @@ pub(crate) fn impl_channels_out_struct(info: &OverseerInfo) -> Result = match message { #( #message_wrapper :: #consumes_variant ( inner ) => { @@ -79,6 +80,13 @@ pub(crate) fn impl_channels_out_struct(info: &OverseerInfo) -> Result Ok(()), + + #[allow(unreachable_patterns)] + // And everything that's not WIP but no subsystem consumes it + unused_msg => { + #support_crate :: gum :: warn!("Nothing consumes {:?}", unused_msg); + Ok(()) + } }; if let Err(subsystem_name) = res { @@ -110,7 +118,14 @@ pub(crate) fn impl_channels_out_struct(info: &OverseerInfo) -> Result Ok(()), )* // dummy message type - #message_wrapper :: Empty => Ok(()) + #message_wrapper :: Empty => Ok(()), + + // And everything that's not WIP but no subsystem consumes it + #[allow(unreachable_patterns)] + unused_msg => { + #support_crate :: gum :: warn!("Nothing consumes {:?}", unused_msg); + Ok(()) + } }; if let Err(subsystem_name) = res { diff --git a/node/overseer/overseer-gen/proc-macro/src/impl_dispatch.rs b/node/overseer/overseer-gen/proc-macro/src/impl_dispatch.rs deleted file mode 100644 index 24cdd2ceed8b..000000000000 --- a/node/overseer/overseer-gen/proc-macro/src/impl_dispatch.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2021 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 . - -use super::*; -use proc_macro2::{Ident, TokenStream}; -use quote::quote; -use syn::Path; - -pub(crate) fn impl_dispatch(info: &OverseerInfo) -> TokenStream { - let message_wrapper = &info.message_wrapper; - - let dispatchable_variant = info - .subsystems() - .into_iter() - .filter(|ssf| !ssf.no_dispatch) - .filter(|ssf| !ssf.wip) - .map(|ssf| ssf.generic.clone()) - .collect::>(); - - let dispatchable_message = info - .subsystems() - .into_iter() - .filter(|ssf| !ssf.no_dispatch) - .filter(|ssf| !ssf.wip) - .map(|ssf| ssf.consumes.clone()) - .collect::>(); - - let mut ts = TokenStream::new(); - if let Some(extern_network_ty) = &info.extern_network_ty.clone() { - ts.extend(quote! { - impl #message_wrapper { - /// Generated dispatch iterator generator. - pub fn dispatch_iter(extern_msg: #extern_network_ty) -> impl Iterator + Send { - [ - #( - extern_msg - // focuses on a `NetworkBridgeEvent< protocol_v1::* >` - // TODO do not require this to be hardcoded, either externalize or ... - // https://github.com/paritytech/polkadot/issues/3427 - .focus() - .ok() - .map(|event| { - #message_wrapper :: #dispatchable_variant ( - // the inner type of the enum variant - #dispatchable_message :: from( event ) - ) - }), - )* - ] - .into_iter() - .filter_map(|x: Option<_>| x) - } - } - }); - } - ts -} diff --git a/node/overseer/overseer-gen/proc-macro/src/impl_message_wrapper.rs b/node/overseer/overseer-gen/proc-macro/src/impl_message_wrapper.rs index 6de017e80055..7d29ddd458ab 100644 --- a/node/overseer/overseer-gen/proc-macro/src/impl_message_wrapper.rs +++ b/node/overseer/overseer-gen/proc-macro/src/impl_message_wrapper.rs @@ -21,7 +21,7 @@ use super::*; /// Generates the wrapper type enum. pub(crate) fn impl_message_wrapper_enum(info: &OverseerInfo) -> Result { - let consumes = info.consumes(); + let consumes = info.any_message(); let consumes_variant = info.variant_names(); let outgoing = &info.outgoing_ty; @@ -52,7 +52,8 @@ pub(crate) fn impl_message_wrapper_enum(info: &OverseerInfo) -> Result. - -use quote::quote; -use syn::Ident; - -use super::*; - -/// Implement a builder pattern for the `Overseer`-type, -/// which acts as the gateway to constructing the overseer. -pub(crate) fn impl_misc(info: &OverseerInfo) -> proc_macro2::TokenStream { - let overseer_name = info.overseer_name.clone(); - let subsystem_sender_name = - Ident::new(&(overseer_name.to_string() + "SubsystemSender"), overseer_name.span()); - let subsystem_ctx_name = - Ident::new(&(overseer_name.to_string() + "SubsystemContext"), overseer_name.span()); - let consumes = &info.consumes(); - let signal = &info.extern_signal_ty; - let wrapper_message = &info.message_wrapper; - let error_ty = &info.extern_error_ty; - let support_crate = info.support_crate_name(); - - let ts = quote! { - /// Connector to send messages towards all subsystems, - /// while tracking the which signals where already received. - #[derive(Debug, Clone)] - pub struct #subsystem_sender_name { - /// Collection of channels to all subsystems. - channels: ChannelsOut, - /// Systemwide tick for which signals were received by all subsystems. - signals_received: SignalsReceived, - } - - /// implementation for wrapping message type... - #[#support_crate ::async_trait] - impl SubsystemSender< #wrapper_message > for #subsystem_sender_name { - async fn send_message(&mut self, msg: #wrapper_message) { - self.channels.send_and_log_error(self.signals_received.load(), msg).await; - } - - async fn send_messages(&mut self, msgs: T) - where - T: IntoIterator + Send, - T::IntoIter: Send, - { - // This can definitely be optimized if necessary. - for msg in msgs { - self.send_message(msg).await; - } - } - - fn send_unbounded_message(&mut self, msg: #wrapper_message) { - self.channels.send_unbounded_and_log_error(self.signals_received.load(), msg); - } - } - - // ... but also implement for all individual messages to avoid - // the necessity for manual wrapping, and do the conversion - // based on the generated `From::from` impl for the individual variants. - #( - #[#support_crate ::async_trait] - impl SubsystemSender< #consumes > for #subsystem_sender_name { - async fn send_message(&mut self, msg: #consumes) { - self.channels.send_and_log_error(self.signals_received.load(), #wrapper_message ::from ( msg )).await; - } - - async fn send_messages(&mut self, msgs: T) - where - T: IntoIterator + Send, - T::IntoIter: Send, - { - // This can definitely be optimized if necessary. - for msg in msgs { - self.send_message(msg).await; - } - } - - fn send_unbounded_message(&mut self, msg: #consumes) { - self.channels.send_unbounded_and_log_error(self.signals_received.load(), #wrapper_message ::from ( msg )); - } - } - )* - - /// A context type that is given to the [`Subsystem`] upon spawning. - /// It can be used by [`Subsystem`] to communicate with other [`Subsystem`]s - /// or to spawn it's [`SubsystemJob`]s. - /// - /// [`Overseer`]: struct.Overseer.html - /// [`Subsystem`]: trait.Subsystem.html - /// [`SubsystemJob`]: trait.SubsystemJob.html - #[derive(Debug)] - #[allow(missing_docs)] - pub struct #subsystem_ctx_name{ - signals: #support_crate ::metered::MeteredReceiver< #signal >, - messages: SubsystemIncomingMessages, - to_subsystems: #subsystem_sender_name, - to_overseer: #support_crate ::metered::UnboundedMeteredSender< - #support_crate ::ToOverseer - >, - signals_received: SignalsReceived, - pending_incoming: Option<(usize, M)>, - name: &'static str - } - - impl #subsystem_ctx_name { - /// Create a new context. - fn new( - signals: #support_crate ::metered::MeteredReceiver< #signal >, - messages: SubsystemIncomingMessages, - to_subsystems: ChannelsOut, - to_overseer: #support_crate ::metered::UnboundedMeteredSender<#support_crate:: ToOverseer>, - name: &'static str - ) -> Self { - let signals_received = SignalsReceived::default(); - #subsystem_ctx_name { - signals, - messages, - to_subsystems: #subsystem_sender_name { - channels: to_subsystems, - signals_received: signals_received.clone(), - }, - to_overseer, - signals_received, - pending_incoming: None, - name - } - } - - fn name(&self) -> &'static str { - self.name - } - } - - #[#support_crate ::async_trait] - impl #support_crate ::SubsystemContext for #subsystem_ctx_name - where - #subsystem_sender_name: #support_crate ::SubsystemSender< #wrapper_message >, - #wrapper_message: From, - { - type Message = M; - type Signal = #signal; - type Sender = #subsystem_sender_name; - type AllMessages = #wrapper_message; - type Error = #error_ty; - - async fn try_recv(&mut self) -> ::std::result::Result>, ()> { - match #support_crate ::poll!(self.recv()) { - #support_crate ::Poll::Ready(msg) => Ok(Some(msg.map_err(|_| ())?)), - #support_crate ::Poll::Pending => Ok(None), - } - } - - async fn recv(&mut self) -> ::std::result::Result, #error_ty> { - loop { - // If we have a message pending an overseer signal, we only poll for signals - // in the meantime. - if let Some((needs_signals_received, msg)) = self.pending_incoming.take() { - if needs_signals_received <= self.signals_received.load() { - return Ok(#support_crate ::FromOverseer::Communication { msg }); - } else { - self.pending_incoming = Some((needs_signals_received, msg)); - - // wait for next signal. - let signal = self.signals.next().await - .ok_or(#support_crate ::OverseerError::Context( - "Signal channel is terminated and empty." - .to_owned() - ))?; - - self.signals_received.inc(); - return Ok(#support_crate ::FromOverseer::Signal(signal)) - } - } - - let mut await_message = self.messages.next().fuse(); - let mut await_signal = self.signals.next().fuse(); - let signals_received = self.signals_received.load(); - let pending_incoming = &mut self.pending_incoming; - - // Otherwise, wait for the next signal or incoming message. - let from_overseer = #support_crate ::futures::select_biased! { - signal = await_signal => { - let signal = signal - .ok_or(#support_crate ::OverseerError::Context( - "Signal channel is terminated and empty." - .to_owned() - ))?; - - #support_crate ::FromOverseer::Signal(signal) - } - msg = await_message => { - let packet = msg - .ok_or(#support_crate ::OverseerError::Context( - "Message channel is terminated and empty." - .to_owned() - ))?; - - if packet.signals_received > signals_received { - // wait until we've received enough signals to return this message. - *pending_incoming = Some((packet.signals_received, packet.message)); - continue; - } else { - // we know enough to return this message. - #support_crate ::FromOverseer::Communication { msg: packet.message} - } - } - }; - - if let #support_crate ::FromOverseer::Signal(_) = from_overseer { - self.signals_received.inc(); - } - - return Ok(from_overseer); - } - } - - fn sender(&mut self) -> &mut Self::Sender { - &mut self.to_subsystems - } - - fn spawn(&mut self, name: &'static str, s: Pin + Send>>) - -> ::std::result::Result<(), #error_ty> - { - self.to_overseer.unbounded_send(#support_crate ::ToOverseer::SpawnJob { - name, - subsystem: Some(self.name()), - s, - }).map_err(|_| #support_crate ::OverseerError::TaskSpawn(name))?; - Ok(()) - } - - fn spawn_blocking(&mut self, name: &'static str, s: Pin + Send>>) - -> ::std::result::Result<(), #error_ty> - { - self.to_overseer.unbounded_send(#support_crate ::ToOverseer::SpawnBlockingJob { - name, - subsystem: Some(self.name()), - s, - }).map_err(|_| #support_crate ::OverseerError::TaskSpawn(name))?; - Ok(()) - } - } - }; - - ts -} diff --git a/node/overseer/overseer-gen/proc-macro/src/impl_overseer.rs b/node/overseer/overseer-gen/proc-macro/src/impl_overseer.rs index 4721eb5373e1..b3bb74665687 100644 --- a/node/overseer/overseer-gen/proc-macro/src/impl_overseer.rs +++ b/node/overseer/overseer-gen/proc-macro/src/impl_overseer.rs @@ -37,7 +37,7 @@ pub(crate) fn impl_overseer_struct(info: &OverseerInfo) -> proc_macro2::TokenStr S: #support_crate ::SpawnNamed, }; // TODO add `where ..` clauses for baggage types - // TODO https://github.com/paritytech/polkadot/issues/3427 + // TODO let consumes = &info.consumes_without_wip(); let consumes_variant = &info.variant_names_without_wip(); @@ -148,6 +148,12 @@ pub(crate) fn impl_overseer_struct(info: &OverseerInfo) -> proc_macro2::TokenStr #message_wrapper :: #unconsumes_variant ( _ ) => {} )* #message_wrapper :: Empty => {} + + // And everything that's not WIP but no subsystem consumes it + #[allow(unreachable_patterns)] + unused_msg => { + #support_crate :: gum :: warn!("Nothing consumes {:?}", unused_msg); + } } Ok(()) } diff --git a/node/overseer/overseer-gen/proc-macro/src/impl_subsystem_ctx_sender.rs b/node/overseer/overseer-gen/proc-macro/src/impl_subsystem_ctx_sender.rs new file mode 100644 index 000000000000..9bb051a38703 --- /dev/null +++ b/node/overseer/overseer-gen/proc-macro/src/impl_subsystem_ctx_sender.rs @@ -0,0 +1,712 @@ +// Copyright 2022 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 . + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{Ident, Path, Result, Type}; + +use petgraph::{ + dot::{self, Dot}, + graph::NodeIndex, + visit::EdgeRef, + Direction, +}; +use std::collections::HashMap; + +use super::*; + +/// Render a graphviz (aka dot graph) to a file. +fn graphviz( + graph: &petgraph::Graph, + dest: &mut impl std::io::Write, +) -> std::io::Result<()> { + let config = &[dot::Config::EdgeNoLabel, dot::Config::NodeNoLabel][..]; + let dot = Dot::with_attr_getters( + graph, + config, + &|_graph, edge| -> String { + format!( + r#"label="{}""#, + edge.weight().get_ident().expect("Must have a trailing identifier. qed") + ) + }, + &|_graph, (_node_index, subsystem_name)| -> String { + format!(r#"label="{}""#, subsystem_name,) + }, + ); + dest.write_all(format!("{:?}", &dot).as_bytes())?; + Ok(()) +} + +/// Generates all subsystem types and related accumulation traits. +pub(crate) fn impl_subsystem_types_all(info: &OverseerInfo) -> Result { + let mut ts = TokenStream::new(); + + let overseer_name = &info.overseer_name; + let span = overseer_name.span(); + let all_messages_wrapper = &info.message_wrapper; + let support_crate = info.support_crate_name(); + let signal_ty = &info.extern_signal_ty; + let error_ty = &info.extern_error_ty; + + // create a directed graph with all the subsystems as nodes and the messages as edges + // key is always the message path, values are node indices in the graph and the subsystem generic identifier + // store the message path and the source sender, both in the graph as well as identifier + let mut outgoing_lut = HashMap::<&Path, Vec<(Ident, NodeIndex)>>::with_capacity(128); + // same for consuming the incoming messages + let mut consuming_lut = HashMap::<&Path, (Ident, NodeIndex)>::with_capacity(128); + + // Ident = Node = subsystem generic names + // Path = Edge = messages + let mut graph = petgraph::Graph::::new(); + + // prepare the full index of outgoing and source subsystems + for ssf in info.subsystems() { + let node_index = graph.add_node(ssf.generic.clone()); + for outgoing in ssf.messages_to_send.iter() { + outgoing_lut + .entry(outgoing) + .or_default() + .push((ssf.generic.clone(), node_index)); + } + consuming_lut.insert(&ssf.message_to_consume, (ssf.generic.clone(), node_index)); + } + + for (message_ty, (_consuming_subsystem_ident, consuming_node_index)) in consuming_lut.iter() { + // match the outgoing ones that were registered above with the consumed message + if let Some(origin_subsystems) = outgoing_lut.get(message_ty) { + for (_origin_subsystem_ident, sending_node_index) in origin_subsystems.iter() { + graph.add_edge(*sending_node_index, *consuming_node_index, (*message_ty).clone()); + } + } + } + + // All outgoing edges are now usable to derive everything we need + for node_index in graph.node_indices() { + let subsystem_name = graph[node_index].to_string(); + let outgoing_wrapper = Ident::new(&(subsystem_name + "OutgoingMessages"), span); + + // cannot be a hashmap, duplicate keys and sorting required + // maps outgoing messages to the subsystem that consumes it + let outgoing_to_consumer = graph + .edges_directed(node_index, Direction::Outgoing) + .map(|edge| { + let message_ty = edge.weight(); + let subsystem_generic_consumer = graph[edge.target()].clone(); + Ok((to_variant(message_ty, span.clone())?, subsystem_generic_consumer)) + }) + .collect::>>()?; + + // Split it for usage with quote + let outgoing_variant = outgoing_to_consumer.iter().map(|x| x.0.clone()).collect::>(); + let subsystem_generic = outgoing_to_consumer.into_iter().map(|x| x.1).collect::>(); + + ts.extend(quote! { + impl ::std::convert::From< #outgoing_wrapper > for #all_messages_wrapper { + fn from(message: #outgoing_wrapper) -> Self { + match message { + #( + #outgoing_wrapper :: #outgoing_variant ( msg ) => #all_messages_wrapper :: #subsystem_generic ( msg ), + )* + #outgoing_wrapper :: Empty => #all_messages_wrapper :: Empty, + // And everything that's not WIP but no subsystem consumes it + #[allow(unreachable_patterns)] + unused_msg => { + #support_crate :: gum :: warn!("Nothing consumes {:?}", unused_msg); + #all_messages_wrapper :: Empty + } + } + } + } + }) + } + + // Dump the graph to file. + if cfg!(feature = "graph") || true { + let path = std::path::PathBuf::from(env!("OUT_DIR")) + .join(overseer_name.to_string().to_lowercase() + "-subsystem-messaging.dot"); + if let Err(e) = std::fs::OpenOptions::new() + .truncate(true) + .create(true) + .write(true) + .open(&path) + .and_then(|mut f| graphviz(&graph, &mut f)) + { + eprintln!("Failed to write dot graph to {}: {:?}", path.display(), e); + } else { + println!("Wrote dot graph to {}", path.display()); + } + } + + let subsystem_sender_name = &Ident::new(&(overseer_name.to_string() + "Sender"), span); + let subsystem_ctx_name = &Ident::new(&(overseer_name.to_string() + "SubsystemContext"), span); + ts.extend(impl_subsystem_context(info, &subsystem_sender_name, &subsystem_ctx_name)); + + ts.extend(impl_associate_outgoing_messages_trait(&all_messages_wrapper)); + + ts.extend(impl_subsystem_sender( + support_crate, + info.subsystems().iter().map(|ssf| { + let outgoing_wrapper = + Ident::new(&(ssf.generic.to_string() + "OutgoingMessages"), span); + outgoing_wrapper + }), + &all_messages_wrapper, + &subsystem_sender_name, + )); + + // Create all subsystem specific types, one by one + for ssf in info.subsystems() { + let subsystem_name = ssf.generic.to_string(); + let outgoing_wrapper = &Ident::new(&(subsystem_name.clone() + "OutgoingMessages"), span); + + let subsystem_ctx_trait = &Ident::new(&(subsystem_name.clone() + "ContextTrait"), span); + let subsystem_sender_trait = &Ident::new(&(subsystem_name.clone() + "SenderTrait"), span); + + ts.extend(impl_per_subsystem_helper_traits( + info, + subsystem_ctx_name, + subsystem_ctx_trait, + subsystem_sender_name, + subsystem_sender_trait, + &ssf.message_to_consume, + &ssf.messages_to_send, + outgoing_wrapper, + )); + + ts.extend(impl_associate_outgoing_messages(&ssf.message_to_consume, &outgoing_wrapper)); + + ts.extend(impl_wrapper_enum(&outgoing_wrapper, ssf.messages_to_send.as_slice())?); + } + + // impl the emtpy tuple handling for tests + let empty_tuple: Type = parse_quote! { () }; + ts.extend(impl_subsystem_context_trait_for( + empty_tuple.clone(), + &[], + empty_tuple, + all_messages_wrapper, + subsystem_ctx_name, + subsystem_sender_name, + support_crate, + signal_ty, + error_ty, + )); + + Ok(ts) +} + +/// Extract the final component of the message type path as used in the `#[subsystem(consumes: path::to::Foo)]` annotation. +fn to_variant(path: &Path, span: Span) -> Result { + let ident = path + .segments + .last() + .ok_or_else(|| syn::Error::new(span, "Path is empty, but it must end with an identifier")) + .map(|segment| segment.ident.clone())?; + Ok(ident) +} + +/// Converts the outgoing message types to variants. +/// +/// Note: Commonly this is `${X}Message` becomes `${X}OutgoingMessages::${X}Message` +/// where for `AllMessages` it would be `AllMessages::${X}`. +fn to_variants(message_types: &[Path], span: Span) -> Result> { + let variants: Vec<_> = + Result::from_iter(message_types.into_iter().map(|path| to_variant(path, span.clone())))?; + Ok(variants) +} + +/// Generates the wrapper type enum, no bells or whistles. +pub(crate) fn impl_wrapper_enum(wrapper: &Ident, message_types: &[Path]) -> Result { + // The message types are path based, each of them must finish with a type + // and as such we do this upfront. + let variants = to_variants(message_types, wrapper.span())?; + + let ts = quote! { + #[allow(missing_docs)] + #[derive(Debug)] + pub enum #wrapper { + #( + #variants ( #message_types ), + )* + Empty, + } + + #( + impl ::std::convert::From< #message_types > for #wrapper { + fn from(message: #message_types) -> Self { + #wrapper :: #variants ( message ) + } + } + )* + + // Useful for unit and integration tests: + impl ::std::convert::From< () > for #wrapper { + fn from(_message: ()) -> Self { + #wrapper :: Empty + } + } + }; + Ok(ts) +} + +/// Create the subsystem sender type and implements `trait SubsystemSender` +/// for the `#outgoing_wrappers: From` with the proper associated types. +pub(crate) fn impl_subsystem_sender( + support_crate: &Path, + outgoing_wrappers: impl IntoIterator, + all_messages_wrapper: &Ident, + subsystem_sender_name: &Ident, +) -> TokenStream { + let mut ts = quote! { + /// Connector to send messages towards all subsystems, + /// while tracking the which signals where already received. + #[derive(Debug)] + pub struct #subsystem_sender_name < OutgoingWrapper > { + /// Collection of channels to all subsystems. + channels: ChannelsOut, + /// Systemwide tick for which signals were received by all subsystems. + signals_received: SignalsReceived, + /// Keep that marker around. + _phantom: ::core::marker::PhantomData< OutgoingWrapper >, + } + + // can't derive due to `PhantomData` and `OutgoingWrapper` not being + // bounded by `Clone`. + impl std::clone::Clone for #subsystem_sender_name < OutgoingWrapper > { + fn clone(&self) -> Self { + Self { + channels: self.channels.clone(), + signals_received: self.signals_received.clone(), + _phantom: ::core::marker::PhantomData::default(), + } + } + } + }; + + // Create the same for a wrapping enum: + // + // 1. subsystem specific `*OutgoingMessages`-type + // 2. overseer-global-`AllMessages`-type + let wrapped = |outgoing_wrapper: &TokenStream| { + quote! { + #[#support_crate ::async_trait] + impl SubsystemSender< OutgoingMessage > for #subsystem_sender_name < #outgoing_wrapper > + where + OutgoingMessage: Send + 'static, + #outgoing_wrapper: ::std::convert::From + Send, + #all_messages_wrapper: ::std::convert::From< #outgoing_wrapper > + Send, + { + async fn send_message(&mut self, msg: OutgoingMessage) + { + self.channels.send_and_log_error( + self.signals_received.load(), + <#all_messages_wrapper as ::std::convert::From<_>> ::from ( + <#outgoing_wrapper as ::std::convert::From<_>> :: from ( msg ) + ) + ).await; + } + + async fn send_messages(&mut self, msgs: I) + where + I: IntoIterator + Send, + I::IntoIter: Iterator + Send, + { + for msg in msgs { + self.send_message( msg ).await; + } + } + + fn send_unbounded_message(&mut self, msg: OutgoingMessage) + { + self.channels.send_unbounded_and_log_error( + self.signals_received.load(), + <#all_messages_wrapper as ::std::convert::From<_>> ::from ( + <#outgoing_wrapper as ::std::convert::From<_>> :: from ( msg ) + ) + ); + } + } + } + }; + + for outgoing_wrapper in outgoing_wrappers { + ts.extend(wrapped("e! { + #outgoing_wrapper + })); + } + + ts.extend(wrapped("e! { + () + })); + + ts +} + +/// Define the `trait AssociateOutgoing` and implement it for `#all_messages_wrapper` and `()`. +pub(crate) fn impl_associate_outgoing_messages_trait(all_messages_wrapper: &Ident) -> TokenStream { + quote! { + /// Binds a generated type covering all declared outgoing messages, + /// which implements `#generated_outgoing: From` for all annotated types + /// of a particular subsystem. + /// + /// Note: This works because there is a 1?:1 relation between consumed messages and subsystems. + pub trait AssociateOutgoing: ::std::fmt::Debug + Send { + /// The associated _outgoing_ messages for a subsystem that _consumes_ the message `Self`. + type OutgoingMessages: Into< #all_messages_wrapper > + ::std::fmt::Debug + Send; + } + + // Helper for tests, where nothing is ever sent. + impl AssociateOutgoing for () { + type OutgoingMessages = (); + } + + // Helper for tests, allows sending of arbitrary messages give + // an test context. + impl AssociateOutgoing for #all_messages_wrapper { + type OutgoingMessages = #all_messages_wrapper ; + } + } +} + +/// Implement `AssociateOutgoing` for `#consumes` being handled by a particular subsystem. +/// +/// Binds the outgoing messages to the inbound message. +/// +/// Note: Works, since there is a 1:1 relation between inbound message type and subsystem declarations. +/// Note: A workaround until default associated types work in `rustc`. +pub(crate) fn impl_associate_outgoing_messages( + consumes: &Path, + outgoing_wrapper: &Ident, +) -> TokenStream { + quote! { + impl AssociateOutgoing for #outgoing_wrapper { + type OutgoingMessages = #outgoing_wrapper; + } + + impl AssociateOutgoing for #consumes { + type OutgoingMessages = #outgoing_wrapper; + } + } +} + +/// Implement `trait SubsystemContext` for a particular subsystem context, +/// that is generated by the proc-macro too. +pub(crate) fn impl_subsystem_context_trait_for( + consumes: Type, + outgoing: &[Type], + outgoing_wrapper: Type, + all_messages_wrapper: &Ident, + subsystem_ctx_name: &Ident, + subsystem_sender_name: &Ident, + support_crate: &Path, + signal: &Path, + error_ty: &Path, +) -> TokenStream { + // impl the subsystem context trait + let where_clause = quote! { + #consumes: AssociateOutgoing + ::std::fmt::Debug + Send + 'static, + #all_messages_wrapper: From< #outgoing_wrapper >, + #all_messages_wrapper: From< #consumes >, + #outgoing_wrapper: #( From< #outgoing > )+*, + }; + + quote! { + #[#support_crate ::async_trait] + impl #support_crate ::SubsystemContext for #subsystem_ctx_name < #consumes > + where + #where_clause + { + type Message = #consumes; + type Signal = #signal; + type OutgoingMessages = #outgoing_wrapper; + type Sender = #subsystem_sender_name < #outgoing_wrapper >; + type Error = #error_ty; + + async fn try_recv(&mut self) -> ::std::result::Result>, ()> { + match #support_crate ::poll!(self.recv()) { + #support_crate ::Poll::Ready(msg) => Ok(Some(msg.map_err(|_| ())?)), + #support_crate ::Poll::Pending => Ok(None), + } + } + + async fn recv(&mut self) -> ::std::result::Result, #error_ty> { + loop { + // If we have a message pending an overseer signal, we only poll for signals + // in the meantime. + if let Some((needs_signals_received, msg)) = self.pending_incoming.take() { + if needs_signals_received <= self.signals_received.load() { + return Ok( #support_crate ::FromOverseer::Communication { msg }); + } else { + self.pending_incoming = Some((needs_signals_received, msg)); + + // wait for next signal. + let signal = self.signals.next().await + .ok_or(#support_crate ::OverseerError::Context( + "Signal channel is terminated and empty." + .to_owned() + ))?; + + self.signals_received.inc(); + return Ok( #support_crate ::FromOverseer::Signal(signal)) + } + } + + let mut await_message = self.messages.next().fuse(); + let mut await_signal = self.signals.next().fuse(); + let signals_received = self.signals_received.load(); + let pending_incoming = &mut self.pending_incoming; + + // Otherwise, wait for the next signal or incoming message. + let from_overseer = #support_crate ::futures::select_biased! { + signal = await_signal => { + let signal = signal + .ok_or( #support_crate ::OverseerError::Context( + "Signal channel is terminated and empty." + .to_owned() + ))?; + + #support_crate ::FromOverseer::Signal(signal) + } + msg = await_message => { + let packet = msg + .ok_or( #support_crate ::OverseerError::Context( + "Message channel is terminated and empty." + .to_owned() + ))?; + + if packet.signals_received > signals_received { + // wait until we've received enough signals to return this message. + *pending_incoming = Some((packet.signals_received, packet.message)); + continue; + } else { + // we know enough to return this message. + #support_crate ::FromOverseer::Communication { msg: packet.message} + } + } + }; + + if let #support_crate ::FromOverseer::Signal(_) = from_overseer { + self.signals_received.inc(); + } + + return Ok(from_overseer); + } + } + + fn sender(&mut self) -> &mut Self::Sender { + &mut self.to_subsystems + } + + fn spawn(&mut self, name: &'static str, s: Pin + Send>>) + -> ::std::result::Result<(), #error_ty> + { + self.to_overseer.unbounded_send(#support_crate ::ToOverseer::SpawnJob { + name, + subsystem: Some(self.name()), + s, + }).map_err(|_| #support_crate ::OverseerError::TaskSpawn(name))?; + Ok(()) + } + + fn spawn_blocking(&mut self, name: &'static str, s: Pin + Send>>) + -> ::std::result::Result<(), #error_ty> + { + self.to_overseer.unbounded_send(#support_crate ::ToOverseer::SpawnBlockingJob { + name, + subsystem: Some(self.name()), + s, + }).map_err(|_| #support_crate ::OverseerError::TaskSpawn(name))?; + Ok(()) + } + } + } +} + +/// Implement the additional subsystem accumulation traits, for simplified usage, +/// i.e. `${Subsystem}SenderTrait` and `${Subsystem}ContextTrait`. +pub(crate) fn impl_per_subsystem_helper_traits( + info: &OverseerInfo, + subsystem_ctx_name: &Ident, + subsystem_ctx_trait: &Ident, + subsystem_sender_name: &Ident, + subsystem_sender_trait: &Ident, + consumes: &Path, + outgoing: &[Path], + outgoing_wrapper: &Ident, +) -> TokenStream { + let all_messages_wrapper = &info.message_wrapper; + let signal_ty = &info.extern_signal_ty; + let error_ty = &info.extern_error_ty; + let support_crate = info.support_crate_name(); + + let mut ts = TokenStream::new(); + + // Create a helper trait bound of all outgoing messages, and the generated wrapper type + // for ease of use within subsystems: + let acc_sender_trait_bounds = quote! { + #support_crate ::SubsystemSender< #outgoing_wrapper > + #( + + #support_crate ::SubsystemSender< #outgoing > + )* + + #support_crate ::SubsystemSender< () > + + Send + + 'static + }; + + ts.extend(quote! { + /// A abstracting trait for usage with subsystems. + pub trait #subsystem_sender_trait : #acc_sender_trait_bounds + {} + + impl #subsystem_sender_trait for T + where + T: #acc_sender_trait_bounds + {} + }); + + // Create a helper accumulated per subsystem trait bound: + let where_clause = quote! { + #consumes: AssociateOutgoing + ::std::fmt::Debug + Send + 'static, + #all_messages_wrapper: From< #outgoing_wrapper >, + #all_messages_wrapper: From< #consumes >, + #all_messages_wrapper: From< () >, + #outgoing_wrapper: #( From< #outgoing > )+*, + #outgoing_wrapper: From< () >, + }; + + ts.extend(quote! { + /// Accumulative trait for a particular subsystem wrapper. + pub trait #subsystem_ctx_trait : SubsystemContext < + Message = #consumes, + Signal = #signal_ty, + OutgoingMessages = #outgoing_wrapper, + // Sender, + Error = #error_ty, + > + where + #where_clause + ::Sender: + #subsystem_sender_trait + + #acc_sender_trait_bounds, + { + /// Sender. + type Sender: #subsystem_sender_trait; + } + + impl #subsystem_ctx_trait for T + where + T: SubsystemContext < + Message = #consumes, + Signal = #signal_ty, + OutgoingMessages = #outgoing_wrapper, + // Sender + Error = #error_ty, + >, + #where_clause + ::Sender: + #subsystem_sender_trait + + #acc_sender_trait_bounds, + { + type Sender = ::Sender; + } + }); + + ts.extend(impl_subsystem_context_trait_for( + parse_quote! { #consumes }, + &Vec::from_iter(outgoing.iter().map(|path| { + parse_quote! { #path } + })), + parse_quote! { #outgoing_wrapper }, + all_messages_wrapper, + subsystem_ctx_name, + subsystem_sender_name, + support_crate, + signal_ty, + error_ty, + )); + ts +} + +/// Generate the subsystem context type and provide `fn new` on it. +/// +/// Note: The generated `fn new` is used by the [builder pattern](../impl_builder.rs). +pub(crate) fn impl_subsystem_context( + info: &OverseerInfo, + subsystem_sender_name: &Ident, + subsystem_ctx_name: &Ident, +) -> TokenStream { + let signal_ty = &info.extern_signal_ty; + let support_crate = info.support_crate_name(); + + let ts = quote! { + /// A context type that is given to the [`Subsystem`] upon spawning. + /// It can be used by [`Subsystem`] to communicate with other [`Subsystem`]s + /// or to spawn it's [`SubsystemJob`]s. + /// + /// [`Overseer`]: struct.Overseer.html + /// [`Subsystem`]: trait.Subsystem.html + /// [`SubsystemJob`]: trait.SubsystemJob.html + #[derive(Debug)] + #[allow(missing_docs)] + pub struct #subsystem_ctx_name { + signals: #support_crate ::metered::MeteredReceiver< #signal_ty >, + messages: SubsystemIncomingMessages< M >, + to_subsystems: #subsystem_sender_name < ::OutgoingMessages >, + to_overseer: #support_crate ::metered::UnboundedMeteredSender< + #support_crate ::ToOverseer + >, + signals_received: SignalsReceived, + pending_incoming: Option<(usize, M)>, + name: &'static str + } + + impl #subsystem_ctx_name + where + M: AssociateOutgoing + Send + 'static, + { + /// Create a new context. + fn new( + signals: #support_crate ::metered::MeteredReceiver< #signal_ty >, + messages: SubsystemIncomingMessages< M >, + to_subsystems: ChannelsOut, + to_overseer: #support_crate ::metered::UnboundedMeteredSender<#support_crate:: ToOverseer>, + name: &'static str + ) -> Self { + let signals_received = SignalsReceived::default(); + #subsystem_ctx_name :: { + signals, + messages, + to_subsystems: #subsystem_sender_name :: < ::OutgoingMessages > { + channels: to_subsystems, + signals_received: signals_received.clone(), + _phantom: ::core::marker::PhantomData::default(), + }, + to_overseer, + signals_received, + pending_incoming: None, + name + } + } + + fn name(&self) -> &'static str { + self.name + } + } + }; + + ts +} diff --git a/node/overseer/overseer-gen/proc-macro/src/lib.rs b/node/overseer/overseer-gen/proc-macro/src/lib.rs index a0275e3e7987..7845917cfbab 100644 --- a/node/overseer/overseer-gen/proc-macro/src/lib.rs +++ b/node/overseer/overseer-gen/proc-macro/src/lib.rs @@ -14,32 +14,43 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -#![deny(unused_crate_dependencies)] - use proc_macro2::{Ident, Span, TokenStream}; -use quote::{quote, ToTokens}; -use syn::{parse2, Result}; +use syn::{parse_quote, spanned::Spanned, Path}; mod impl_builder; mod impl_channels_out; -mod impl_dispatch; mod impl_message_wrapper; -mod impl_misc; mod impl_overseer; -mod parse_attr; -mod parse_struct; +mod impl_subsystem_ctx_sender; +mod overseer; +mod parse; +mod subsystem; + +#[cfg(test)] +mod tests; use impl_builder::*; use impl_channels_out::*; -use impl_dispatch::*; use impl_message_wrapper::*; -use impl_misc::*; use impl_overseer::*; -use parse_attr::*; -use parse_struct::*; +use impl_subsystem_ctx_sender::*; +use parse::*; -#[cfg(test)] -mod tests; +use self::{overseer::*, subsystem::*}; + +/// Obtain the support crate `Path` as `TokenStream`. +pub(crate) fn support_crate() -> Result { + Ok(if cfg!(test) { + parse_quote! {crate} + } else { + use proc_macro_crate::{crate_name, FoundCrate}; + let crate_name = crate_name("polkadot-overseer-gen")?; + match crate_name { + FoundCrate::Itself => parse_quote! {crate}, + FoundCrate::Name(name) => Ident::new(&name, Span::call_site()).into(), + } + }) +} #[proc_macro_attribute] pub fn overlord( @@ -53,58 +64,26 @@ pub fn overlord( .into() } -pub(crate) fn impl_overseer_gen( - attr: TokenStream, - orig: TokenStream, -) -> Result { - let args: AttrArgs = parse2(attr)?; - let message_wrapper = args.message_wrapper; - - let of: OverseerGuts = parse2(orig)?; - - let support_crate_name = if cfg!(test) { - quote! {crate} - } else { - use proc_macro_crate::{crate_name, FoundCrate}; - let crate_name = crate_name("polkadot-overseer-gen") - .expect("Support crate polkadot-overseer-gen is present in `Cargo.toml`. qed"); - match crate_name { - FoundCrate::Itself => quote! {crate}, - FoundCrate::Name(name) => Ident::new(&name, Span::call_site()).to_token_stream(), - } - }; - let info = OverseerInfo { - support_crate_name, - subsystems: of.subsystems, - baggage: of.baggage, - overseer_name: of.name, - message_wrapper, - message_channel_capacity: args.message_channel_capacity, - signal_channel_capacity: args.signal_channel_capacity, - extern_event_ty: args.extern_event_ty, - extern_signal_ty: args.extern_signal_ty, - extern_error_ty: args.extern_error_ty, - extern_network_ty: args.extern_network_ty, - outgoing_ty: args.outgoing_ty, - }; - - let mut additive = impl_overseer_struct(&info); - additive.extend(impl_builder(&info)); - - additive.extend(impl_overseen_subsystem(&info)); - additive.extend(impl_channels_out_struct(&info)); - additive.extend(impl_misc(&info)); - - additive.extend(impl_message_wrapper_enum(&info)?); - additive.extend(impl_dispatch(&info)); - - let ts = expander::Expander::new("overlord-expansion") - .add_comment("Generated overseer code by `#[overlord(..)]`".to_owned()) - .dry(!cfg!(feature = "expand")) - .verbose(false) - .fmt(expander::Edition::_2021) - .write_to_out_dir(additive) - .expect("Expander does not fail due to IO in OUT_DIR. qed"); +#[proc_macro_attribute] +pub fn subsystem( + attr: proc_macro::TokenStream, + item: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let attr: TokenStream = attr.into(); + let item: TokenStream = item.into(); + impl_subsystem_context_trait_bounds(attr, item, MakeSubsystem::ImplSubsystemTrait) + .unwrap_or_else(|err| err.to_compile_error()) + .into() +} - Ok(ts) +#[proc_macro_attribute] +pub fn contextbounds( + attr: proc_macro::TokenStream, + item: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let attr: TokenStream = attr.into(); + let item: TokenStream = item.into(); + impl_subsystem_context_trait_bounds(attr, item, MakeSubsystem::AddContextTraitBounds) + .unwrap_or_else(|err| err.to_compile_error()) + .into() } diff --git a/node/overseer/overseer-gen/proc-macro/src/overseer.rs b/node/overseer/overseer-gen/proc-macro/src/overseer.rs new file mode 100644 index 000000000000..127120337ae0 --- /dev/null +++ b/node/overseer/overseer-gen/proc-macro/src/overseer.rs @@ -0,0 +1,67 @@ +// Copyright 2022 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 . + +use proc_macro2::TokenStream; +use syn::{parse2, Result}; + +use super::{parse::*, *}; + +pub(crate) fn impl_overseer_gen( + attr: TokenStream, + orig: TokenStream, +) -> Result { + let args: OverseerAttrArgs = parse2(attr)?; + let message_wrapper = args.message_wrapper; + + let of: OverseerGuts = parse2(orig)?; + + let support_crate = support_crate().expect("The crate this macro is run for, includes the proc-macro support as dependency, otherwise it could not be run in the first place. qed"); + let info = OverseerInfo { + support_crate, + subsystems: of.subsystems, + baggage: of.baggage, + overseer_name: of.name, + message_wrapper, + message_channel_capacity: args.message_channel_capacity, + signal_channel_capacity: args.signal_channel_capacity, + extern_event_ty: args.extern_event_ty, + extern_signal_ty: args.extern_signal_ty, + extern_error_ty: args.extern_error_ty, + outgoing_ty: args.outgoing_ty, + }; + + let mut additive = impl_overseer_struct(&info); + additive.extend(impl_builder(&info)); + + additive.extend(impl_overseen_subsystem(&info)); + additive.extend(impl_channels_out_struct(&info)); + additive.extend(impl_subsystem_types_all(&info)?); + + additive.extend(impl_message_wrapper_enum(&info)?); + + let ts = expander::Expander::new("overlord-expansion") + .add_comment("Generated overseer code by `#[overlord(..)]`".to_owned()) + .dry(!cfg!(feature = "expand")) + .verbose(true) + // once all our needed format options are available on stable + // we should enabled this again, until then too many warnings + // are generated + // .fmt(expander::Edition::_2021) + .write_to_out_dir(additive) + .expect("Expander does not fail due to IO in OUT_DIR. qed"); + + Ok(ts) +} diff --git a/node/overseer/overseer-gen/proc-macro/src/parse/mod.rs b/node/overseer/overseer-gen/proc-macro/src/parse/mod.rs new file mode 100644 index 000000000000..f1d82ff93451 --- /dev/null +++ b/node/overseer/overseer-gen/proc-macro/src/parse/mod.rs @@ -0,0 +1,39 @@ +// Copyright 2022 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 . + +mod kw { + syn::custom_keyword!(event); + syn::custom_keyword!(signal); + syn::custom_keyword!(error); + syn::custom_keyword!(outgoing); + syn::custom_keyword!(gen); + syn::custom_keyword!(signal_capacity); + syn::custom_keyword!(message_capacity); + syn::custom_keyword!(subsystem); + syn::custom_keyword!(prefix); +} + +mod parse_overseer_attr; +mod parse_overseer_struct; + +mod parse_subsystem_attr; + +#[cfg(test)] +mod tests; + +pub(crate) use self::{parse_overseer_attr::*, parse_overseer_struct::*}; + +pub(crate) use self::parse_subsystem_attr::*; diff --git a/node/overseer/overseer-gen/proc-macro/src/parse_attr.rs b/node/overseer/overseer-gen/proc-macro/src/parse/parse_overseer_attr.rs similarity index 85% rename from node/overseer/overseer-gen/proc-macro/src/parse_attr.rs rename to node/overseer/overseer-gen/proc-macro/src/parse/parse_overseer_attr.rs index e688f84f2e37..e5cb30dd7513 100644 --- a/node/overseer/overseer-gen/proc-macro/src/parse_attr.rs +++ b/node/overseer/overseer-gen/proc-macro/src/parse/parse_overseer_attr.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . +use super::kw; use proc_macro2::Span; use quote::{quote, ToTokens}; use std::collections::{hash_map::RandomState, HashMap}; @@ -24,21 +25,9 @@ use syn::{ Error, Ident, LitInt, Path, Result, Token, }; -mod kw { - syn::custom_keyword!(event); - syn::custom_keyword!(signal); - syn::custom_keyword!(error); - syn::custom_keyword!(network); - syn::custom_keyword!(outgoing); - syn::custom_keyword!(gen); - syn::custom_keyword!(signal_capacity); - syn::custom_keyword!(message_capacity); -} - #[derive(Clone, Debug)] enum OverseerAttrItem { ExternEventType { tag: kw::event, eq_token: Token![=], value: Path }, - ExternNetworkType { tag: kw::network, eq_token: Token![=], value: Path }, ExternOverseerSignalType { tag: kw::signal, eq_token: Token![=], value: Path }, ExternErrorType { tag: kw::error, eq_token: Token![=], value: Path }, OutgoingType { tag: kw::outgoing, eq_token: Token![=], value: Path }, @@ -53,9 +42,6 @@ impl ToTokens for OverseerAttrItem { Self::ExternEventType { tag, eq_token, value } => { quote! { #tag #eq_token, #value } }, - Self::ExternNetworkType { tag, eq_token, value } => { - quote! { #tag #eq_token, #value } - }, Self::ExternOverseerSignalType { tag, eq_token, value } => { quote! { #tag #eq_token, #value } }, @@ -100,12 +86,6 @@ impl Parse for OverseerAttrItem { eq_token: input.parse()?, value: input.parse()?, }) - } else if lookahead.peek(kw::network) { - Ok(OverseerAttrItem::ExternNetworkType { - tag: input.parse::()?, - eq_token: input.parse()?, - value: input.parse()?, - }) } else if lookahead.peek(kw::outgoing) { Ok(OverseerAttrItem::OutgoingType { tag: input.parse::()?, @@ -138,15 +118,11 @@ impl Parse for OverseerAttrItem { /// Attribute arguments #[derive(Clone, Debug)] -pub(crate) struct AttrArgs { +pub(crate) struct OverseerAttrArgs { pub(crate) message_wrapper: Ident, pub(crate) extern_event_ty: Path, pub(crate) extern_signal_ty: Path, pub(crate) extern_error_ty: Path, - /// A external subsystem that both consumes and produces messages - /// but is not part of the band of subsystems, it's a mere proxy - /// to another entity that consumes/produces messages. - pub(crate) extern_network_ty: Option, pub(crate) outgoing_ty: Option, pub(crate) signal_channel_capacity: usize, pub(crate) message_channel_capacity: usize, @@ -170,7 +146,7 @@ macro_rules! extract_variant { }; } -impl Parse for AttrArgs { +impl Parse for OverseerAttrArgs { fn parse(input: &ParseBuffer) -> Result { let items: Punctuated = input.parse_terminated(OverseerAttrItem::parse)?; @@ -198,18 +174,16 @@ impl Parse for AttrArgs { let error = extract_variant!(unique, ExternErrorType; err = "Must declare the overseer error type via `error=..`.")?; let event = extract_variant!(unique, ExternEventType; err = "Must declare the overseer event type via `event=..`.")?; - let signal = extract_variant!(unique, ExternOverseerSignalType; err = "Must declare the overseer signal type via `span=..`.")?; + let signal = extract_variant!(unique, ExternOverseerSignalType; err = "Must declare the overseer signal type via `signal=..`.")?; let message_wrapper = extract_variant!(unique, MessageWrapperName; err = "Must declare the overseer generated wrapping message type via `gen=..`.")?; - let network = extract_variant!(unique, ExternNetworkType); let outgoing = extract_variant!(unique, OutgoingType); - Ok(AttrArgs { + Ok(OverseerAttrArgs { signal_channel_capacity, message_channel_capacity, extern_event_ty: event, extern_signal_ty: signal, extern_error_ty: error, - extern_network_ty: network, outgoing_ty: outgoing, message_wrapper, }) diff --git a/node/overseer/overseer-gen/proc-macro/src/parse_struct.rs b/node/overseer/overseer-gen/proc-macro/src/parse/parse_overseer_struct.rs similarity index 67% rename from node/overseer/overseer-gen/proc-macro/src/parse_struct.rs rename to node/overseer/overseer-gen/proc-macro/src/parse/parse_overseer_struct.rs index 2eed4001d6eb..179a4ada06eb 100644 --- a/node/overseer/overseer-gen/proc-macro/src/parse_struct.rs +++ b/node/overseer/overseer-gen/proc-macro/src/parse/parse_overseer_struct.rs @@ -17,32 +17,36 @@ use proc_macro2::{Span, TokenStream}; use std::collections::{hash_map::RandomState, HashMap, HashSet}; use syn::{ + parenthesized, parse::{Parse, ParseStream}, punctuated::Punctuated, spanned::Spanned, - AttrStyle, Attribute, Error, Field, FieldsNamed, GenericParam, Ident, ItemStruct, Path, Result, - Token, Type, Visibility, + token::Bracket, + AttrStyle, Error, Field, FieldsNamed, GenericParam, Ident, ItemStruct, Path, Result, Token, + Type, Visibility, }; use quote::{quote, ToTokens}; mod kw { syn::custom_keyword!(wip); - syn::custom_keyword!(no_dispatch); syn::custom_keyword!(blocking); + syn::custom_keyword!(consumes); + syn::custom_keyword!(sends); } #[derive(Clone, Debug)] -enum SubSysAttrItem { +pub(crate) enum SubSysAttrItem { /// The subsystem is still a work in progress /// and should not be communicated with. Wip(kw::wip), /// The subsystem is blocking and requires to be /// spawned on an exclusive thread. Blocking(kw::blocking), - /// External messages should not be - after being converted - - /// be dispatched to the annotated subsystem. - NoDispatch(kw::no_dispatch), + /// Message to be sent by this subsystem. + Sends(Sends), + /// Message to be consumed by this subsystem. + Consumes(Consumes), } impl Parse for SubSysAttrItem { @@ -52,10 +56,10 @@ impl Parse for SubSysAttrItem { Self::Wip(input.parse::()?) } else if lookahead.peek(kw::blocking) { Self::Blocking(input.parse::()?) - } else if lookahead.peek(kw::no_dispatch) { - Self::NoDispatch(input.parse::()?) + } else if lookahead.peek(kw::sends) { + Self::Sends(input.parse::()?) } else { - return Err(lookahead.error()) + Self::Consumes(input.parse::()?) }) } } @@ -69,8 +73,11 @@ impl ToTokens for SubSysAttrItem { Self::Blocking(blocking) => { quote! { #blocking } }, - Self::NoDispatch(no_dispatch) => { - quote! { #no_dispatch } + Self::Sends(_) => { + quote! {} + }, + Self::Consumes(_) => { + quote! {} }, }; tokens.extend(ts.into_iter()); @@ -78,7 +85,7 @@ impl ToTokens for SubSysAttrItem { } /// A field of the struct annotated with -/// `#[subsystem(no_dispatch, , A | B | C)]` +/// `#[subsystem(A, B, C)]` #[derive(Clone, Debug)] pub(crate) struct SubSysField { /// Name of the field. @@ -87,11 +94,10 @@ pub(crate) struct SubSysField { /// which is also used `#wrapper_message :: #variant` variant /// part. pub(crate) generic: Ident, - /// Type to be consumed by the subsystem. - pub(crate) consumes: Path, - /// If `no_dispatch` is present, if the message is incoming via - /// an `extern` `Event`, it will not be dispatched to all subsystems. - pub(crate) no_dispatch: bool, + /// Type of message to be consumed by the subsystem. + pub(crate) message_to_consume: Path, + /// Types of messages to be sent by the subsystem. + pub(crate) messages_to_send: Vec, /// If the subsystem implementation is blocking execution and hence /// has to be spawned on a separate thread or thread pool. pub(crate) blocking: bool, @@ -115,6 +121,15 @@ macro_rules! extract_variant { ($unique:expr, $variant:ident ; err = $err:expr) => { extract_variant!($unique, $variant).ok_or_else(|| Error::new(Span::call_site(), $err)) }; + ($unique:expr, $variant:ident take) => { + $unique.values().find_map(|item| { + if let SubSysAttrItem::$variant(value) = item { + Some(value.clone()) + } else { + None + } + }) + }; ($unique:expr, $variant:ident) => { $unique.values().find_map(|item| { if let SubSysAttrItem::$variant(_) = item { @@ -126,57 +141,113 @@ macro_rules! extract_variant { }; } -pub(crate) struct SubSystemTags { +#[derive(Debug, Clone)] +pub(crate) struct Sends { + #[allow(dead_code)] + pub(crate) keyword_sends: kw::sends, #[allow(dead_code)] - pub(crate) attrs: Vec, + pub(crate) colon: Token![:], #[allow(dead_code)] - pub(crate) no_dispatch: bool, + pub(crate) bracket: Option, + pub(crate) sends: Punctuated, +} + +impl Parse for Sends { + fn parse(input: syn::parse::ParseStream) -> Result { + let content; + let keyword_sends = input.parse()?; + let colon = input.parse()?; + let (bracket, sends) = if !input.peek(syn::token::Bracket) { + let mut sends = Punctuated::new(); + sends.push_value(input.parse::()?); + (None, sends) + } else { + let bracket = Some(syn::bracketed!(content in input)); + let sends = Punctuated::parse_terminated(&content)?; + (bracket, sends) + }; + Ok(Self { keyword_sends, colon, bracket, sends }) + } +} + +#[derive(Debug, Clone)] +pub(crate) struct Consumes { + #[allow(dead_code)] + pub(crate) keyword_consumes: Option, + #[allow(dead_code)] + pub(crate) colon: Option, + pub(crate) consumes: Path, +} + +impl Parse for Consumes { + fn parse(input: syn::parse::ParseStream) -> Result { + let lookahead = input.lookahead1(); + Ok(if lookahead.peek(kw::consumes) { + Self { + keyword_consumes: Some(input.parse()?), + colon: input.parse()?, + consumes: input.parse()?, + } + } else { + Self { keyword_consumes: None, colon: None, consumes: input.parse()? } + }) + } +} + +/// Parses `(Foo, sends = [Bar, Baz])` +/// including the `(` and `)`. +#[derive(Debug, Clone)] +pub(crate) struct SubSystemAttrItems { /// The subsystem is in progress, only generate the `Wrapper` variant, but do not forward messages /// and also not include the subsystem in the list of subsystems. pub(crate) wip: bool, + /// If there are blocking components in the subsystem and hence it should be + /// spawned on a dedicated thread pool for such subssytems. pub(crate) blocking: bool, - pub(crate) consumes: Path, + /// The message type being consumed by the subsystem. + pub(crate) consumes: Option, + pub(crate) sends: Option, } -impl Parse for SubSystemTags { +impl Parse for SubSystemAttrItems { fn parse(input: syn::parse::ParseStream) -> Result { - let attrs = Attribute::parse_outer(input)?; + let span = input.span(); - let input = input; let content; - let _ = syn::parenthesized!(content in input); - - let mut items = Punctuated::new(); - while let Ok(tag) = content.call(SubSysAttrItem::parse) { - items.push_value(tag); - items.push_punct(content.call(::parse)?); - } + let _paren_token = parenthesized!(content in input); - assert!(items.empty_or_trailing(), "Always followed by the message type to consume. qed"); - - let consumes = content.parse::()?; + let items = content.call(Punctuated::::parse_terminated)?; let mut unique = HashMap::< std::mem::Discriminant, SubSysAttrItem, RandomState, >::default(); + for item in items { if let Some(first) = unique.insert(std::mem::discriminant(&item), item.clone()) { - let mut e = Error::new( - item.span(), - format!("Duplicate definition of subsystem attribute found"), - ); + let mut e = + Error::new(item.span(), "Duplicate definition of subsystem attribute found"); e.combine(Error::new(first.span(), "previously defined here.")); return Err(e) } } - let no_dispatch = extract_variant!(unique, NoDispatch; default = false); + // A subsystem makes no sense if not one of them is provided + let sends = extract_variant!(unique, Sends take); + let consumes = extract_variant!(unique, Consumes take); + if sends.as_ref().map(|sends| sends.sends.is_empty()).unwrap_or(true) && consumes.is_none() + { + return Err(Error::new( + span, + "Must have at least one of `consumes: [..]` and `sends: [..]`.", + )) + } + let blocking = extract_variant!(unique, Blocking; default = false); let wip = extract_variant!(unique, Wip; default = false); - Ok(Self { attrs, no_dispatch, blocking, consumes, wip }) + Ok(Self { blocking, wip, sends, consumes }) } } @@ -192,7 +263,7 @@ pub(crate) struct BaggageField { #[derive(Clone, Debug)] pub(crate) struct OverseerInfo { /// Where the support crate `::polkadot_overseer_gen` lives. - pub(crate) support_crate_name: TokenStream, + pub(crate) support_crate: Path, /// Fields annotated with `#[subsystem(..)]`. pub(crate) subsystems: Vec, @@ -216,11 +287,8 @@ pub(crate) struct OverseerInfo { /// Incoming event type from the outer world, usually an external framework of some sort. pub(crate) extern_event_ty: Path, - /// Incoming event type from an external entity, commonly from the network. - pub(crate) extern_network_ty: Option, - /// Type of messages that are sent to an external subsystem. - /// Merely here to be included during generation of `message_wrapper` type. + /// Merely here to be included during generation of `#message_wrapper` type. pub(crate) outgoing_ty: Option, /// Incoming event type from the outer world, commonly from the network. @@ -228,8 +296,8 @@ pub(crate) struct OverseerInfo { } impl OverseerInfo { - pub(crate) fn support_crate_name(&self) -> &TokenStream { - &self.support_crate_name + pub(crate) fn support_crate_name(&self) -> &Path { + &self.support_crate } pub(crate) fn variant_names(&self) -> Vec { @@ -297,8 +365,11 @@ impl OverseerInfo { .collect::>() } - pub(crate) fn consumes(&self) -> Vec { - self.subsystems.iter().map(|ssf| ssf.consumes.clone()).collect::>() + pub(crate) fn any_message(&self) -> Vec { + self.subsystems + .iter() + .map(|ssf| ssf.message_to_consume.clone()) + .collect::>() } pub(crate) fn channel_names_without_wip(&self, suffix: &'static str) -> Vec { @@ -313,7 +384,7 @@ impl OverseerInfo { self.subsystems .iter() .filter(|ssf| !ssf.wip) - .map(|ssf| ssf.consumes.clone()) + .map(|ssf| ssf.message_to_consume.clone()) .collect::>() } } @@ -341,7 +412,8 @@ impl OverseerGuts { // for the builder pattern besides other places. let mut unique_subsystem_idents = HashSet::::new(); for Field { attrs, vis, ident, ty, .. } in fields.named.into_iter() { - let mut consumes = + // collect all subsystem annotations per field + let mut subsystem_attr = attrs.iter().filter(|attr| attr.style == AttrStyle::Outer).filter_map(|attr| { let span = attr.path.span(); attr.path.get_ident().filter(|ident| *ident == "subsystem").map(move |_ident| { @@ -349,53 +421,75 @@ impl OverseerGuts { (attr_tokens, span) }) }); - let ident = - ident.ok_or_else(|| Error::new(ty.span(), "Missing identifier for member. BUG"))?; - - if let Some((attr_tokens, span)) = consumes.next() { - if let Some((_attr_tokens2, span2)) = consumes.next() { + let ident = ident.ok_or_else(|| { + Error::new( + ty.span(), + "Missing identifier for field, only named fields are expceted.", + ) + })?; + + // a `#[subsystem(..)]` annotation exists + if let Some((attr_tokens, span)) = subsystem_attr.next() { + if let Some((_attr_tokens2, span2)) = subsystem_attr.next() { return Err({ let mut err = Error::new(span, "The first subsystem annotation is at"); err.combine(Error::new(span2, "but another here for the same field.")); err }) } - let mut consumes_paths = Vec::with_capacity(attrs.len()); + + let span = attr_tokens.span(); + let attr_tokens = attr_tokens.clone(); - let variant: SubSystemTags = syn::parse2(attr_tokens.clone())?; - consumes_paths.push(variant.consumes); + let subsystem_attrs: SubSystemAttrItems = syn::parse2(attr_tokens.clone())?; let field_ty = try_type_to_path(ty, span)?; let generic = field_ty .get_ident() .ok_or_else(|| { - Error::new(field_ty.span(), "Must be an identifier, not a path.") + Error::new( + field_ty.span(), + "Must be an identifier, not a path. It will be used as a generic.", + ) })? .clone(); + // check for unique subsystem name, otherwise we'd create invalid code: if let Some(previous) = unique_subsystem_idents.get(&generic) { - let mut e = Error::new( - generic.span(), - format!("Duplicate subsystem names `{}`", generic), - ); + let mut e = Error::new(generic.span(), "Duplicate subsystem names"); e.combine(Error::new(previous.span(), "previously defined here.")); return Err(e) } unique_subsystem_idents.insert(generic.clone()); + let SubSystemAttrItems { wip, blocking, consumes, sends, .. } = subsystem_attrs; + + // messages to be sent + let sends = if let Some(sends) = sends { + Vec::from_iter(sends.sends.iter().cloned()) + } else { + vec![] + }; + // messages deemed for consumption + let consumes = if let Some(consumes) = consumes { + consumes.consumes + } else { + return Err(Error::new(span, "Must provide exactly one consuming message type")) + }; + subsystems.push(SubSysField { name: ident, generic, - consumes: consumes_paths[0].clone(), - no_dispatch: variant.no_dispatch, - wip: variant.wip, - blocking: variant.blocking, + message_to_consume: consumes, + messages_to_send: sends, + wip, + blocking, }); } else { let field_ty = try_type_to_path(ty, ident.span())?; let generic = field_ty .get_ident() .map(|ident| baggage_generics.contains(ident)) - .unwrap_or_default(); + .unwrap_or(false); baggage.push(BaggageField { field_name: ident, generic, field_ty, vis }); } } diff --git a/node/overseer/overseer-gen/proc-macro/src/parse/parse_subsystem_attr.rs b/node/overseer/overseer-gen/proc-macro/src/parse/parse_subsystem_attr.rs new file mode 100644 index 000000000000..ec308a6b804f --- /dev/null +++ b/node/overseer/overseer-gen/proc-macro/src/parse/parse_subsystem_attr.rs @@ -0,0 +1,144 @@ +// Copyright 2022 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 . + +use super::kw; +use proc_macro2::Span; +use quote::{quote, ToTokens}; +use std::collections::{hash_map::RandomState, HashMap}; +use syn::{ + parse::{Parse, ParseBuffer}, + punctuated::Punctuated, + spanned::Spanned, + Error, Ident, Path, Result, Token, +}; + +#[derive(Clone, Debug)] +enum SubsystemAttrItem { + /// Error type provided by the user. + Error { tag: kw::error, eq_token: Token![=], value: Path }, + /// For which slot in the overseer this should be plugged. + /// + /// The subsystem implementation can and should have a different name + /// from the declared parameter type in the overseer. + Subsystem { tag: Option, eq_token: Option, value: Ident }, + /// The prefix to apply when a subsystem is implemented in a different file/crate + /// than the overseer itself. + /// + /// Important for `#[subsystem(..)]` to reference the traits correctly. + TraitPrefix { tag: kw::prefix, eq_token: Token![=], value: Path }, +} + +impl ToTokens for SubsystemAttrItem { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let ts = match self { + Self::TraitPrefix { tag, eq_token, value } => { + quote! { #tag #eq_token, #value } + }, + Self::Error { tag, eq_token, value } => { + quote! { #tag #eq_token, #value } + }, + Self::Subsystem { tag, eq_token, value } => { + quote! { #tag #eq_token, #value } + }, + }; + tokens.extend(ts.into_iter()); + } +} + +impl Parse for SubsystemAttrItem { + fn parse(input: &ParseBuffer) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(kw::error) { + Ok(SubsystemAttrItem::Error { + tag: input.parse::()?, + eq_token: input.parse()?, + value: input.parse()?, + }) + } else if lookahead.peek(kw::prefix) { + Ok(SubsystemAttrItem::TraitPrefix { + tag: input.parse::()?, + eq_token: input.parse()?, + value: input.parse()?, + }) + } else if lookahead.peek(kw::subsystem) { + Ok(SubsystemAttrItem::Subsystem { + tag: Some(input.parse::()?), + eq_token: Some(input.parse()?), + value: input.parse()?, + }) + } else { + Ok(SubsystemAttrItem::Subsystem { tag: None, eq_token: None, value: input.parse()? }) + } + } +} + +/// Attribute arguments `$args` in `#[subsystem( $args )]`. +#[derive(Clone, Debug)] +pub(crate) struct SubsystemAttrArgs { + span: Span, + pub(crate) error_path: Option, + pub(crate) subsystem_ident: Ident, + pub(crate) trait_prefix_path: Option, +} + +impl Spanned for SubsystemAttrArgs { + fn span(&self) -> Span { + self.span.clone() + } +} + +macro_rules! extract_variant { + ($unique:expr, $variant:ident ; default = $fallback:expr) => { + extract_variant!($unique, $variant).unwrap_or_else(|| $fallback) + }; + ($unique:expr, $variant:ident ; err = $err:expr) => { + extract_variant!($unique, $variant).ok_or_else(|| Error::new(Span::call_site(), $err)) + }; + ($unique:expr, $variant:ident) => { + $unique.values().find_map(|item| match item { + SubsystemAttrItem::$variant { value, .. } => Some(value.clone()), + _ => None, + }) + }; +} + +impl Parse for SubsystemAttrArgs { + fn parse(input: &ParseBuffer) -> Result { + let span = input.span(); + let items: Punctuated = + input.parse_terminated(SubsystemAttrItem::parse)?; + + let mut unique = HashMap::< + std::mem::Discriminant, + SubsystemAttrItem, + RandomState, + >::default(); + for item in items { + if let Some(first) = unique.insert(std::mem::discriminant(&item), item.clone()) { + let mut e = Error::new( + item.span(), + format!("Duplicate definition of subsystem generation type found"), + ); + e.combine(Error::new(first.span(), "previously defined here.")); + return Err(e) + } + } + let error_path = extract_variant!(unique, Error); + let subsystem_ident = extract_variant!(unique, Subsystem; err = "Must annotate the identical overseer error type via `subsystem=..` or plainly as `Subsystem` as specified in the overseer declaration.")?; + let trait_prefix_path = extract_variant!(unique, TraitPrefix); + Ok(SubsystemAttrArgs { span, error_path, subsystem_ident, trait_prefix_path }) + } +} diff --git a/node/overseer/overseer-gen/proc-macro/src/parse/tests.rs b/node/overseer/overseer-gen/proc-macro/src/parse/tests.rs new file mode 100644 index 000000000000..990b5d6872c0 --- /dev/null +++ b/node/overseer/overseer-gen/proc-macro/src/parse/tests.rs @@ -0,0 +1,295 @@ +// Copyright 2022 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 . + +use super::*; +use crate::{SubSysAttrItem, SubSystemAttrItems}; +use assert_matches::assert_matches; +use quote::quote; +use syn::parse_quote; + +mod attr { + use super::*; + + #[test] + fn attr_full_works() { + let attr: OverseerAttrArgs = parse_quote! { + gen=AllMessage, event=::some::why::ExternEvent, signal=SigSigSig, signal_capacity=111, message_capacity=222, + error=OverseerError, + }; + assert_matches!(attr, OverseerAttrArgs { + message_channel_capacity, + signal_channel_capacity, + .. + } => { + assert_eq!(message_channel_capacity, 222); + assert_eq!(signal_channel_capacity, 111); + }); + } + + #[test] + fn attr_partial_works() { + let attr: OverseerAttrArgs = parse_quote! { + gen=AllMessage, event=::some::why::ExternEvent, signal=::foo::SigSigSig, + error=OverseerError, + }; + assert_matches!(attr, OverseerAttrArgs { + message_channel_capacity: _, + signal_channel_capacity: _, + .. + } => { + }); + } +} + +mod strukt { + + use super::*; + + #[test] + fn parse_subsystem_attr_item_works_00_wip() { + assert_matches!( + syn::parse2::(quote! { + wip + }), Ok(SubSysAttrItem::Wip(_)) => { + }); + } + + #[test] + fn parse_subsystem_attr_item_works_02_sends() { + assert_matches!( + syn::parse2::(quote! { + sends: [A, B, C] + }), Ok(SubSysAttrItem::Sends(sends)) => { + assert_eq!(sends.sends.len(), 3); + }); + } + + #[test] + fn parse_subsystem_attr_item_works_03_sends() { + assert_matches!( + syn::parse2::(quote! { + sends: [A] + }), Ok(SubSysAttrItem::Sends(sends)) => { + assert_eq!(sends.sends.len(), 1); + }); + } + + #[test] + fn parse_subsystem_attr_item_works_04_sends() { + assert_matches!( + syn::parse2::(quote! { + sends: [A,] + }), Ok(SubSysAttrItem::Sends(sends)) => { + assert_eq!(sends.sends.len(), 1); + }); + } + + #[test] + fn parse_subsystem_attr_item_works_05_sends() { + assert_matches!( + syn::parse2::(quote! { + sends: [] + }), Ok(SubSysAttrItem::Sends(sends)) => { + assert_eq!(sends.sends.len(), 0); + }); + } + + #[test] + fn parse_subsystem_attr_item_works_06_consumes() { + assert_matches!( + syn::parse2::(quote! { + consumes: Foo + }), Ok(SubSysAttrItem::Consumes(_consumes)) => { + }); + } + + #[test] + fn parse_subsystem_attr_item_works_07_consumes() { + assert_matches!( + syn::parse2::(quote! { + Foo + }), Ok(SubSysAttrItem::Consumes(_consumes)) => { + }); + } + + #[test] + fn parse_subsystem_attributes_works_00() { + syn::parse2::(quote! { + (wip, blocking, consumes: Foo, sends: []) + }) + .unwrap(); + } + + #[test] + fn parse_subsystem_attributes_works_01() { + assert_matches!( + syn::parse2::(quote! { + (blocking, Foo, sends: []) + }), Ok(_) => { + }); + } + + #[test] + fn parse_subsystem_attributes_works_02() { + assert_matches!( + syn::parse2::(quote! { + (consumes: Foo, sends: [Bar]) + }), Ok(_) => { + }); + } + + #[test] + fn parse_subsystem_attributes_works_03() { + assert_matches!( + syn::parse2::(quote! { + (blocking, consumes: Foo, sends: [Bar]) + }), Ok(_) => { + }); + } + + #[test] + fn parse_subsystem_attributes_works_04() { + assert_matches!( + syn::parse2::(quote! { + (wip, consumes: Foo, sends: [Bar]) + }), Ok(_) => { + }); + } + + #[test] + fn parse_subsystem_attributes_works_05() { + assert_matches!( + syn::parse2::(quote! { + (consumes: Foo) + }), Ok(_) => { + }); + } + + #[test] + fn parse_subsystem_attributes_works_06() { + assert_matches!( + syn::parse2::(quote! { + (sends: [Foo], consumes: Bar) + }), Ok(_) => { + }); + } + + #[test] + fn parse_subsystem_attributes_works_07_duplicate_send() { + assert_matches!( + syn::parse2::(quote! { + (sends: [Foo], Bar, Y) + }), Err(e) => { + dbg!(e) + }); + } + + #[test] + fn parse_subsystem_attributes_works_08() { + assert_matches!( + syn::parse2::(quote! { + (sends: [Foo], consumes: Bar) + }), Ok(_) => { + }); + } + + #[test] + fn parse_subsystem_attributes_works_09_neither_consumes_nor_sends() { + assert_matches!( + syn::parse2::(quote! { + (sends: []) + }), Err(e) => { + // must either consume smth or sends smth, neither is NOK + dbg!(e) + }); + } + + #[test] + fn parse_subsystem_attributes_works_10_empty_with_braces() { + assert_matches!( + syn::parse2::(quote! { + () + }), Err(e) => { + dbg!(e) + }); + } + + #[test] + fn parse_subsystem_attributes_works_11_empty() { + assert_matches!( + syn::parse2::(quote! { + + }), Err(e) => { + dbg!(e) + }); + } + + #[test] + fn parse_subsystem_attributes_works_12_duplicate_consumes_different_fmt() { + assert_matches!( + syn::parse2::(quote! { + (Foo, consumes = Foo) + }), Err(e) => { + dbg!(e) + }); + } + + #[test] + fn struct_parse_baggage() { + let item: OverseerGuts = parse_quote! { + pub struct Ooooh where X: Secrit { + #[subsystem(consumes: Foo, sends: [])] + sub0: FooSubsystem, + + metrics: Metrics, + } + }; + let _ = dbg!(item); + } + + #[test] + fn struct_parse_full() { + let item: OverseerGuts = parse_quote! { + pub struct Ooooh where X: Secrit { + #[subsystem(consumes: Foo, sends: [])] + sub0: FooSubsystem, + + #[subsystem(blocking, consumes: Bar, sends: [])] + yyy: BaersBuyBilliardBalls, + + #[subsystem(blocking, consumes: Twain, sends: [])] + fff: Beeeeep, + + #[subsystem(consumes: Rope)] + mc: MountainCave, + + metrics: Metrics, + } + }; + let _ = dbg!(item); + } + + #[test] + fn struct_parse_basic() { + let item: OverseerGuts = parse_quote! { + pub struct Ooooh { + #[subsystem(consumes: Foo, sends: [])] + sub0: FooSubsystem, + } + }; + let _ = dbg!(item); + } +} diff --git a/node/overseer/overseer-gen/proc-macro/src/subsystem.rs b/node/overseer/overseer-gen/proc-macro/src/subsystem.rs new file mode 100644 index 000000000000..7b5523e4b5c8 --- /dev/null +++ b/node/overseer/overseer-gen/proc-macro/src/subsystem.rs @@ -0,0 +1,310 @@ +// Copyright 2022 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 . + +//! Generates the bounds for a particular subsystem `Context` and associate `type Sender`. +//! +//! +//! ## Implement `trait Subsystem` via `subsystem` +//! +//! ```ignore +//! # use polkadot_overseer_gen_proc_macro::subsystem; +//! # mod somewhere { +//! # use polkadot_overseer_gen_proc_macro::overlord; +//! # pub use polkadot_overseer_gen::*; +//! # +//! # #[derive(Debug, thiserror::Error)] +//! # #[error("Yikes!")] +//! # pub struct Yikes; +//! # impl From for Yikes { +//! # fn from(_: OverseerError) -> Yikes { Yikes } +//! # } +//! # impl From for Yikes { +//! # fn from(_: mpsc::SendError) -> Yikes { Yikes } +//! # } +//! # +//! # #[derive(Debug)] +//! # pub struct Eve; +//! # +//! # #[derive(Debug, Clone)] +//! # pub struct Sig; +//! # +//! # #[derive(Debug, Clone, Copy)] +//! # pub struct A; +//! # #[derive(Debug, Clone, Copy)] +//! # pub struct B; +//! # +//! # #[overlord(signal=Sig, gen=AllOfThem, event=Eve, error=Yikes)] +//! # pub struct Wonderland { +//! # #[subsystem(A, sends: [B])] +//! # foo: Foo, +//! # #[subsystem(B, sends: [A])] +//! # bar: Bar, +//! # } +//! # } +//! # use somewhere::{Yikes, SpawnedSubsystem}; +//! # +//! # struct FooSubsystem; +//! # +//! #[subsystem(Foo, error = Yikes, prefix = somewhere)] +//! impl FooSubsystem { +//! fn start(self, context: Context) -> SpawnedSubsystem { +//! // .. +//! # let _ = context; +//! # unimplemented!() +//! } +//! } +//! ``` +//! +//! expands to +//! +//! ```ignore +//! # use polkadot_overseer_gen_proc_macro::subsystem; +//! # mod somewhere { +//! # use polkadot_overseer_gen_proc_macro::overlord; +//! # pub use polkadot_overseer_gen::*; +//! # +//! # #[derive(Debug, thiserror::Error)] +//! # #[error("Yikes!")] +//! # pub struct Yikes; +//! # impl From for Yikes { +//! # fn from(_: OverseerError) -> Yikes { Yikes } +//! # } +//! # impl From for Yikes { +//! # fn from(_: mpsc::SendError) -> Yikes { Yikes } +//! # } +//! # +//! # #[derive(Debug)] +//! # pub struct Eve; +//! # +//! # #[derive(Debug, Clone)] +//! # pub struct Sig; +//! # +//! # #[derive(Debug, Clone, Copy)] +//! # pub struct A; +//! # #[derive(Debug, Clone, Copy)] +//! # pub struct B; +//! # +//! # #[overlord(signal=Sig, gen=AllOfThem, event=Eve, error=Yikes)] +//! # pub struct Wonderland { +//! # #[subsystem(A, sends: [B])] +//! # foo: Foo, +//! # #[subsystem(B, sends: [A])] +//! # bar: Bar, +//! # } +//! # } +//! # use somewhere::{Yikes, SpawnedSubsystem}; +//! # use polkadot_overseer_gen as support_crate; +//! # +//! # struct FooSubsystem; +//! # +//! impl support_crate::Subsystem for FooSubsystem +//! where +//! Context: somewhere::FooContextTrait, +//! Context: support_crate::SubsystemContext, +//! ::Sender: somewhere::FooSenderTrait, +//! ::Sender: somewhere::FooSenderTrait, +//! { +//! fn start(self, context: Context) -> SpawnedSubsystem { +//! // .. +//! # let _ = context; +//! # unimplemented!() +//! } +//! } +//! ``` +//! +//! where `support_crate` is either equivalent to `somewhere` or derived from the cargo manifest. +//! +//! +//! ## Add additional trait bounds for a generic `Context` via `contextbounds` +//! +//! ### To an `ImplItem` +//! +//! ```ignore +//! #[contextbounds(Foo, prefix = somewhere)] +//! impl X { +//! .. +//! } +//! ``` +//! +//! expands to +//! +//! ```ignore +//! impl X +//! where +//! Context: somewhere::FooSubsystemTrait, +//! Context: support_crate::SubsystemContext, +//! ::Sender: somewhere::FooSenderTrait, +//! ::Sender: somewhere::FooSenderTrait, +//! { +//! } +//! ``` +//! +//! ### To a free standing `Fn` (not a method, that's covered by the above) +//! +//! ```ignore +//! #[contextbounds(Foo, prefix = somewhere)] +//! fn do_smth(context: &mut Context) { +//! .. +//! } +//! ``` +//! +//! expands to +//! +//! ```ignore +//! fn do_smth(context: &mut Context) +//! where +//! Context: somewhere::FooSubsystemTrait, +//! Context: support_crate::SubsystemContext, +//! ::Sender: somewhere::FooSenderTrait, +//! ::Sender: somewhere::FooSenderTrait, +//! { +//! } +//! ``` +use proc_macro2::TokenStream; +use quote::{format_ident, ToTokens}; +use syn::{parse2, parse_quote, punctuated::Punctuated, Result}; + +use super::{parse::*, *}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum MakeSubsystem { + /// Implements `trait Subsystem` and apply the trait bounds to the `Context` generic. + /// + /// Relevant to `impl Item` only. + ImplSubsystemTrait, + /// Only apply the trait bounds to the context. + AddContextTraitBounds, +} + +pub(crate) fn impl_subsystem_context_trait_bounds( + attr: TokenStream, + orig: TokenStream, + make_subsystem: MakeSubsystem, +) -> Result { + let args = parse2::(attr.clone())?; + let span = args.span(); + let SubsystemAttrArgs { error_path, subsystem_ident, trait_prefix_path, .. } = args; + + let mut item = parse2::(orig)?; + + // always prefer the direct usage, if it's not there, let's see if there is + // a `prefix=*` provided. Either is ok. + + // Technically this is two different things: + // The place where the `#[overlord]` is annotated is where all `trait *SenderTrait` and + // `trait *ContextTrait` types exist. + // The other usage is the true support crate `polkadot-overseer-gen`, where the static ones + // are declared. + // Right now, if the `support_crate` is not included, it falls back silently to the `trait_prefix_path`. + let support_crate = support_crate() + .or_else(|_e| { + trait_prefix_path.clone().ok_or_else(|| { + syn::Error::new(attr.span(), "Couldn't find `polkadot-overseer-gen` in manifest, but also missing a `prefix=` to help trait bound resolution") + }) + })?; + + let trait_prefix_path = trait_prefix_path.unwrap_or_else(|| parse_quote! { self }); + if trait_prefix_path.segments.trailing_punct() { + return Err(syn::Error::new(trait_prefix_path.span(), "Must not end with `::`")) + } + + let subsystem_ctx_trait = format_ident!("{}ContextTrait", subsystem_ident); + let subsystem_sender_trait = format_ident!("{}SenderTrait", subsystem_ident); + + let extra_where_predicates: Punctuated = parse_quote! { + Context: #trait_prefix_path::#subsystem_ctx_trait, + Context: #support_crate::SubsystemContext, + ::Sender: #trait_prefix_path::#subsystem_sender_trait, + ::Sender: #trait_prefix_path::#subsystem_sender_trait, + }; + + let apply_ctx_bound_if_present = move |generics: &mut syn::Generics| -> bool { + if generics + .params + .iter() + .find(|generic| match generic { + syn::GenericParam::Type(ty) if ty.ident == "Context" => true, + _ => false, + }) + .is_some() + { + let where_clause = generics.make_where_clause(); + where_clause.predicates.extend(extra_where_predicates.clone()); + true + } else { + false + } + }; + + match item { + syn::Item::Impl(ref mut struktured_impl) => { + if make_subsystem == MakeSubsystem::ImplSubsystemTrait { + let error_path = error_path.ok_or_else(|| { + syn::Error::new( + span, + "Must annotate the identical overseer error type via `error=..`.", + ) + })?; + // Only replace the subsystem trait if it's desired. + struktured_impl.trait_.replace(( + None, + parse_quote! { + #support_crate::Subsystem + }, + syn::token::For::default(), + )); + } + + apply_ctx_bound_if_present(&mut struktured_impl.generics); + for item in struktured_impl.items.iter_mut() { + match item { + syn::ImplItem::Method(method) => { + apply_ctx_bound_if_present(&mut method.sig.generics); + }, + _others => { + // don't error, just nop + }, + } + } + }, + syn::Item::Fn(ref mut struktured_fn) => { + if make_subsystem == MakeSubsystem::ImplSubsystemTrait { + return Err(syn::Error::new(struktured_fn.span(), "Cannot make a free function a subsystem, did you mean to apply `contextbound` instead?")) + } + apply_ctx_bound_if_present(&mut struktured_fn.sig.generics); + }, + other => + return Err(syn::Error::new( + other.span(), + "Macro can only be annotated on functions or struct implementations", + )), + }; + + Ok(item.to_token_stream()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_path() { + let _p: Path = parse_quote! { self }; + let _p: Path = parse_quote! { crate }; + let _p: Path = parse_quote! { ::foo }; + let _p: Path = parse_quote! { bar }; + } +} diff --git a/node/overseer/overseer-gen/proc-macro/src/tests.rs b/node/overseer/overseer-gen/proc-macro/src/tests.rs index 40df210fb6f0..364e53f5b02a 100644 --- a/node/overseer/overseer-gen/proc-macro/src/tests.rs +++ b/node/overseer/overseer-gen/proc-macro/src/tests.rs @@ -32,13 +32,13 @@ fn print() { let item = quote! { pub struct Ooooh where X: Secrit { - #[subsystem(no_dispatch, Foo)] + #[subsystem(Foo)] sub0: FooSubsystem, #[subsystem(blocking, Bar)] yyy: BaersBuyBilliardBalls, - #[subsystem(no_dispatch, blocking, Twain)] + #[subsystem(blocking, Twain)] fff: Beeeeep, #[subsystem(Rope)] @@ -57,13 +57,13 @@ fn print() { fn struct_parse_full() { let item: OverseerGuts = parse_quote! { pub struct Ooooh where X: Secrit { - #[subsystem(no_dispatch, Foo)] + #[subsystem(Foo)] sub0: FooSubsystem, #[subsystem(blocking, Bar)] yyy: BaersBuyBilliardBalls, - #[subsystem(no_dispatch, blocking, Twain)] + #[subsystem(blocking, Twain)] fff: Beeeeep, #[subsystem(Rope)] @@ -88,11 +88,11 @@ fn struct_parse_basic() { #[test] fn attr_full() { - let attr: AttrArgs = parse_quote! { + let attr: OverseerAttrArgs = parse_quote! { gen=AllMessage, event=::some::why::ExternEvent, signal=SigSigSig, signal_capacity=111, message_capacity=222, error=OverseerError, }; - assert_matches!(attr, AttrArgs { + assert_matches!(attr, OverseerAttrArgs { message_channel_capacity, signal_channel_capacity, .. @@ -104,11 +104,11 @@ fn attr_full() { #[test] fn attr_partial() { - let attr: AttrArgs = parse_quote! { + let attr: OverseerAttrArgs = parse_quote! { gen=AllMessage, event=::some::why::ExternEvent, signal=::foo::SigSigSig, error=OverseerError, }; - assert_matches!(attr, AttrArgs { + assert_matches!(attr, OverseerAttrArgs { message_channel_capacity: _, signal_channel_capacity: _, .. diff --git a/node/overseer/overseer-gen/src/lib.rs b/node/overseer/overseer-gen/src/lib.rs index 0d926ead2daf..e0ec74793501 100644 --- a/node/overseer/overseer-gen/src/lib.rs +++ b/node/overseer/overseer-gen/src/lib.rs @@ -60,12 +60,13 @@ #![deny(missing_docs)] #![deny(unused_crate_dependencies)] -pub use polkadot_overseer_gen_proc_macro::overlord; +pub use polkadot_overseer_gen_proc_macro::{contextbounds, overlord, subsystem}; #[doc(hidden)] pub use gum; #[doc(hidden)] pub use metered; + #[doc(hidden)] pub use polkadot_node_primitives::SpawnNamed; @@ -101,7 +102,7 @@ use std::fmt; #[cfg(test)] mod tests; -/// A type of messages that are sent from [`Subsystem`] to [`Overseer`]. +/// A type of messages that are sent from a [`Subsystem`] to the declared overseer. /// /// Used to launch jobs. pub enum ToOverseer { @@ -312,7 +313,7 @@ pub struct SubsystemMeterReadouts { /// /// [`Subsystem`]: trait.Subsystem.html /// -/// `M` here is the inner message type, and _not_ the generated `enum AllMessages`. +/// `M` here is the inner message type, and _not_ the generated `enum AllMessages` or `#message_wrapper` type. pub struct SubsystemInstance { /// Send sink for `Signal`s to be sent to a subsystem. pub tx_signal: crate::metered::MeteredSender, @@ -362,20 +363,23 @@ pub trait SubsystemContext: Send + 'static { /// The message type of this context. Subsystems launched with this context will expect /// to receive messages of this type. Commonly uses the wrapping `enum` commonly called /// `AllMessages`. - type Message: std::fmt::Debug + Send + 'static; + type Message: ::std::fmt::Debug + Send + 'static; /// And the same for signals. - type Signal: std::fmt::Debug + Send + 'static; - /// The overarching all messages `enum`. - /// In some cases can be identical to `Self::Message`. - type AllMessages: From + Send + 'static; + type Signal: ::std::fmt::Debug + Send + 'static; + /// The overarching messages `enum` for this particular subsystem. + type OutgoingMessages: ::std::fmt::Debug + Send + 'static; + + // The overarching messages `enum` for this particular subsystem. + // type AllMessages: From + From + std::fmt::Debug + Send + 'static; + /// The sender type as provided by `sender()` and underlying. - type Sender: SubsystemSender + Send + 'static; + type Sender: Clone + Send + 'static + SubsystemSender; /// The error type. type Error: ::std::error::Error + ::std::convert::From + Sync + Send + 'static; /// Try to asynchronously receive a message. /// - /// This has to be used with caution, if you loop over this without + /// Has to be used with caution, if you loop over this without /// using `pending!()` macro you will end up with a busy loop! async fn try_recv(&mut self) -> Result>, ()>; @@ -397,34 +401,37 @@ pub trait SubsystemContext: Send + 'static { ) -> Result<(), Self::Error>; /// Send a direct message to some other `Subsystem`, routed based on message type. - async fn send_message(&mut self, msg: X) + // #[deprecated(note = "Use `self.sender().send_message(msg) instead, avoid passing around the full context.")] + async fn send_message(&mut self, msg: T) where - Self::AllMessages: From, - X: Send, + Self::OutgoingMessages: From + Send, + T: Send, { - self.sender().send_message(::from(msg)).await + self.sender().send_message(::from(msg)).await } /// Send multiple direct messages to other `Subsystem`s, routed based on message type. - async fn send_messages(&mut self, msgs: T) + // #[deprecated(note = "Use `self.sender().send_message(msg) instead, avoid passing around the full context.")] + async fn send_messages(&mut self, msgs: I) where - T: IntoIterator + Send, - T::IntoIter: Send, - Self::AllMessages: From, - X: Send, + Self::OutgoingMessages: From + Send, + I: IntoIterator + Send, + I::IntoIter: Send, + T: Send, { self.sender() - .send_messages(msgs.into_iter().map(|x| ::from(x))) + .send_messages(msgs.into_iter().map(::from)) .await } /// Send a message using the unbounded connection. + // #[deprecated(note = "Use `self.sender().send_unbounded_message(msg) instead, avoid passing around the full context.")] fn send_unbounded_message(&mut self, msg: X) where - Self::AllMessages: From, + Self::OutgoingMessages: From + Send, X: Send, { - self.sender().send_unbounded_message(Self::AllMessages::from(msg)) + self.sender().send_unbounded_message(::from(msg)) } /// Obtain the sender. @@ -450,22 +457,25 @@ where /// Sender end of a channel to interface with a subsystem. #[async_trait::async_trait] -pub trait SubsystemSender: Send + Clone + 'static { +pub trait SubsystemSender: Clone + Send + 'static +where + OutgoingMessage: Send, +{ /// Send a direct message to some other `Subsystem`, routed based on message type. - async fn send_message(&mut self, msg: Message); + async fn send_message(&mut self, msg: OutgoingMessage); /// Send multiple direct messages to other `Subsystem`s, routed based on message type. - async fn send_messages(&mut self, msgs: T) + async fn send_messages(&mut self, msgs: I) where - T: IntoIterator + Send, - T::IntoIter: Send; + I: IntoIterator + Send, + I::IntoIter: Send; /// Send a message onto the unbounded queue of some other `Subsystem`, routed based on message /// type. /// /// This function should be used only when there is some other bounding factor on the messages /// sent with it. Otherwise, it risks a memory leak. - fn send_unbounded_message(&mut self, msg: Message); + fn send_unbounded_message(&mut self, msg: OutgoingMessage); } /// A future that wraps another future with a `Delay` allowing for time-limited futures. diff --git a/node/overseer/overseer-gen/tests/ui/err-04-missing-error.rs b/node/overseer/overseer-gen/tests/ui/err-04-missing-error.rs index 3b6966f1da79..3547eb36a5c5 100644 --- a/node/overseer/overseer-gen/tests/ui/err-04-missing-error.rs +++ b/node/overseer/overseer-gen/tests/ui/err-04-missing-error.rs @@ -15,7 +15,7 @@ struct MsgStrukt(u8); #[overlord(signal=SigSigSig, event=Event, gen=AllMessages)] struct Overseer { - #[subsystem(no_dispatch, MsgStrukt)] + #[subsystem(MsgStrukt)] sub0: AwesomeSubSys, i_like_pie: f64, diff --git a/node/overseer/overseer-gen/tests/ui/err-05-missing-field.rs b/node/overseer/overseer-gen/tests/ui/err-05-missing-field.rs index a51248b5406a..22ead71ea180 100644 --- a/node/overseer/overseer-gen/tests/ui/err-05-missing-field.rs +++ b/node/overseer/overseer-gen/tests/ui/err-05-missing-field.rs @@ -21,7 +21,7 @@ pub struct MsgStrukt(u8); #[overlord(signal=SigSigSig, error=OverseerError, event=Event, gen=AllMessages)] struct Overseer { - #[subsystem(no_dispatch, MsgStrukt)] + #[subsystem(MsgStrukt)] sub0: AwesomeSubSys, i_like_pie: f64, } @@ -58,4 +58,4 @@ fn main() { .spawner(DummySpawner) .build() .unwrap(); -} \ No newline at end of file +} diff --git a/node/overseer/overseer-gen/tests/ui/err-06-missing-subsystem.rs b/node/overseer/overseer-gen/tests/ui/err-06-missing-subsystem.rs index 540ed366a054..bccc990d5e66 100644 --- a/node/overseer/overseer-gen/tests/ui/err-06-missing-subsystem.rs +++ b/node/overseer/overseer-gen/tests/ui/err-06-missing-subsystem.rs @@ -21,7 +21,7 @@ pub struct MsgStrukt(u8); #[overlord(signal=SigSigSig, error=OverseerError, event=Event, gen=AllMessages)] struct Overseer { - #[subsystem(no_dispatch, MsgStrukt)] + #[subsystem(MsgStrukt)] sub0: AwesomeSubSys, i_like_pie: f64, } diff --git a/node/overseer/overseer-gen/tests/ui/err-07-missing-spawner.rs b/node/overseer/overseer-gen/tests/ui/err-07-missing-spawner.rs index bc25e0c47cff..4193d28ec879 100644 --- a/node/overseer/overseer-gen/tests/ui/err-07-missing-spawner.rs +++ b/node/overseer/overseer-gen/tests/ui/err-07-missing-spawner.rs @@ -21,7 +21,7 @@ pub struct MsgStrukt(u8); #[overlord(signal=SigSigSig, error=OverseerError, event=Event, gen=AllMessages)] struct Overseer { - #[subsystem(no_dispatch, MsgStrukt)] + #[subsystem(MsgStrukt)] sub0: AwesomeSubSys, i_like_pie: f64, } diff --git a/node/overseer/overseer-gen/tests/ui/err-08-duplicate-subsystem.rs b/node/overseer/overseer-gen/tests/ui/err-08-duplicate-subsystem.rs index ea2ad0b4d1ac..8895723abbc1 100644 --- a/node/overseer/overseer-gen/tests/ui/err-08-duplicate-subsystem.rs +++ b/node/overseer/overseer-gen/tests/ui/err-08-duplicate-subsystem.rs @@ -21,7 +21,7 @@ pub struct MsgStrukt(u8); #[overlord(signal=SigSigSig, error=OverseerError, event=Event, gen=AllMessages)] struct Overseer { - #[subsystem(no_dispatch, MsgStrukt)] + #[subsystem(MsgStrukt)] sub0: AwesomeSubSys, i_like_pie: f64, } diff --git a/node/overseer/overseer-gen/tests/ui/err-09-uninit_generic_baggage.rs b/node/overseer/overseer-gen/tests/ui/err-09-uninit_generic_baggage.rs index 06ce22dba351..64a2f6c4bffc 100644 --- a/node/overseer/overseer-gen/tests/ui/err-09-uninit_generic_baggage.rs +++ b/node/overseer/overseer-gen/tests/ui/err-09-uninit_generic_baggage.rs @@ -21,7 +21,7 @@ pub struct MsgStrukt(u8); #[overlord(signal=SigSigSig, error=OverseerError, event=Event, gen=AllMessages)] struct Overseer { - #[subsystem(no_dispatch, MsgStrukt)] + #[subsystem(MsgStrukt)] sub0: AwesomeSubSys, i_like_pie: T, } diff --git a/node/overseer/src/dummy.rs b/node/overseer/src/dummy.rs index eb6b9099a8bb..b4a97c3e6321 100644 --- a/node/overseer/src/dummy.rs +++ b/node/overseer/src/dummy.rs @@ -15,8 +15,8 @@ // along with Polkadot. If not, see . use crate::{ - prometheus::Registry, AllMessages, HeadSupportsParachains, InitializedOverseerBuilder, - MetricsTrait, Overseer, OverseerMetrics, OverseerSignal, OverseerSubsystemContext, SpawnNamed, + prometheus::Registry, HeadSupportsParachains, InitializedOverseerBuilder, MetricsTrait, + Overseer, OverseerMetrics, OverseerSignal, OverseerSubsystemContext, SpawnNamed, KNOWN_LEAVES_CACHE_SIZE, }; use lru::LruCache; @@ -30,11 +30,7 @@ pub struct DummySubsystem; impl Subsystem for DummySubsystem where - Context: SubsystemContext< - Signal = OverseerSignal, - Error = SubsystemError, - AllMessages = AllMessages, - >, + Context: SubsystemContext, { fn start(self, mut ctx: Context) -> SpawnedSubsystem { let future = Box::pin(async move { diff --git a/node/overseer/src/lib.rs b/node/overseer/src/lib.rs index fe7793643e45..bcf486d2a0db 100644 --- a/node/overseer/src/lib.rs +++ b/node/overseer/src/lib.rs @@ -77,15 +77,14 @@ use polkadot_primitives::{ }; use sp_api::{ApiExt, ProvideRuntimeApi}; -use polkadot_node_network_protocol::VersionedValidationProtocol; use polkadot_node_subsystem_types::messages::{ ApprovalDistributionMessage, ApprovalVotingMessage, AvailabilityDistributionMessage, AvailabilityRecoveryMessage, AvailabilityStoreMessage, BitfieldDistributionMessage, BitfieldSigningMessage, CandidateBackingMessage, CandidateValidationMessage, ChainApiMessage, ChainSelectionMessage, CollationGenerationMessage, CollatorProtocolMessage, DisputeCoordinatorMessage, DisputeDistributionMessage, GossipSupportMessage, - NetworkBridgeEvent, NetworkBridgeMessage, ProvisionerMessage, PvfCheckerMessage, - RuntimeApiMessage, StatementDistributionMessage, + NetworkBridgeMessage, ProvisionerMessage, PvfCheckerMessage, RuntimeApiMessage, + StatementDistributionMessage, }; pub use polkadot_node_subsystem_types::{ errors::{SubsystemError, SubsystemResult}, @@ -108,9 +107,9 @@ use parity_util_mem::MemoryAllocationTracker; pub use polkadot_overseer_gen as gen; pub use polkadot_overseer_gen::{ - overlord, FromOverseer, MapSubsystem, MessagePacket, SignalsReceived, SpawnNamed, Subsystem, - SubsystemContext, SubsystemIncomingMessages, SubsystemInstance, SubsystemMeterReadouts, - SubsystemMeters, SubsystemSender, TimeoutExt, ToOverseer, + contextbounds, overlord, subsystem, FromOverseer, MapSubsystem, MessagePacket, SignalsReceived, + SpawnNamed, Subsystem, SubsystemContext, SubsystemIncomingMessages, SubsystemInstance, + SubsystemMeterReadouts, SubsystemMeters, SubsystemSender, TimeoutExt, ToOverseer, }; /// Store 2 days worth of blocks, not accounting for forks, @@ -414,71 +413,155 @@ pub async fn forward_events>(client: Arc

, mut hand event=Event, signal=OverseerSignal, error=SubsystemError, - network=NetworkBridgeEvent, message_capacity=2048, )] pub struct Overseer { - #[subsystem(no_dispatch, CandidateValidationMessage)] + #[subsystem(CandidateValidationMessage, sends: [ + RuntimeApiMessage, + ])] candidate_validation: CandidateValidation, - #[subsystem(no_dispatch, PvfCheckerMessage)] + #[subsystem(PvfCheckerMessage, sends: [ + CandidateValidationMessage, + RuntimeApiMessage, + ])] pvf_checker: PvfChecker, - #[subsystem(no_dispatch, CandidateBackingMessage)] + #[subsystem(CandidateBackingMessage, sends: [ + CandidateValidationMessage, + CollatorProtocolMessage, + AvailabilityDistributionMessage, + AvailabilityStoreMessage, + StatementDistributionMessage, + ProvisionerMessage, + RuntimeApiMessage, + DisputeCoordinatorMessage, + ])] candidate_backing: CandidateBacking, - #[subsystem(StatementDistributionMessage)] + #[subsystem(StatementDistributionMessage, sends: [ + NetworkBridgeMessage, + CandidateBackingMessage, + RuntimeApiMessage, + ])] statement_distribution: StatementDistribution, - #[subsystem(no_dispatch, AvailabilityDistributionMessage)] + #[subsystem(AvailabilityDistributionMessage, sends: [ + AvailabilityStoreMessage, + AvailabilityRecoveryMessage, + ChainApiMessage, + RuntimeApiMessage, + NetworkBridgeMessage, + ])] availability_distribution: AvailabilityDistribution, - #[subsystem(no_dispatch, AvailabilityRecoveryMessage)] + #[subsystem(AvailabilityRecoveryMessage, sends: [ + NetworkBridgeMessage, + RuntimeApiMessage, + AvailabilityStoreMessage, + ])] availability_recovery: AvailabilityRecovery, - #[subsystem(blocking, no_dispatch, BitfieldSigningMessage)] + #[subsystem(blocking, BitfieldSigningMessage, sends: [ + AvailabilityStoreMessage, + RuntimeApiMessage, + BitfieldDistributionMessage, + ])] bitfield_signing: BitfieldSigning, - #[subsystem(BitfieldDistributionMessage)] + #[subsystem(BitfieldDistributionMessage, sends: [ + RuntimeApiMessage, + NetworkBridgeMessage, + ProvisionerMessage, + ])] bitfield_distribution: BitfieldDistribution, - #[subsystem(no_dispatch, ProvisionerMessage)] + #[subsystem(ProvisionerMessage, sends: [ + RuntimeApiMessage, + CandidateBackingMessage, + ChainApiMessage, + DisputeCoordinatorMessage, + ])] provisioner: Provisioner, - #[subsystem(no_dispatch, blocking, RuntimeApiMessage)] + #[subsystem(blocking, RuntimeApiMessage, sends: [])] runtime_api: RuntimeApi, - #[subsystem(no_dispatch, blocking, AvailabilityStoreMessage)] + #[subsystem(blocking, AvailabilityStoreMessage, sends: [ + ChainApiMessage, + RuntimeApiMessage, + ])] availability_store: AvailabilityStore, - #[subsystem(no_dispatch, NetworkBridgeMessage)] + #[subsystem(NetworkBridgeMessage, sends: [ + BitfieldDistributionMessage, + StatementDistributionMessage, + ApprovalDistributionMessage, + GossipSupportMessage, + DisputeDistributionMessage, + CollationGenerationMessage, + CollatorProtocolMessage, + ])] network_bridge: NetworkBridge, - #[subsystem(no_dispatch, blocking, ChainApiMessage)] + #[subsystem(blocking, ChainApiMessage, sends: [])] chain_api: ChainApi, - #[subsystem(no_dispatch, CollationGenerationMessage)] + #[subsystem(CollationGenerationMessage, sends: [ + RuntimeApiMessage, + CollatorProtocolMessage, + ])] collation_generation: CollationGeneration, - #[subsystem(no_dispatch, CollatorProtocolMessage)] + #[subsystem(CollatorProtocolMessage, sends: [ + NetworkBridgeMessage, + RuntimeApiMessage, + CandidateBackingMessage, + ])] collator_protocol: CollatorProtocol, - #[subsystem(ApprovalDistributionMessage)] + #[subsystem(ApprovalDistributionMessage, sends: [ + NetworkBridgeMessage, + ApprovalVotingMessage, + ])] approval_distribution: ApprovalDistribution, - #[subsystem(no_dispatch, blocking, ApprovalVotingMessage)] + #[subsystem(blocking, ApprovalVotingMessage, sends: [ + RuntimeApiMessage, + ChainApiMessage, + ChainSelectionMessage, + DisputeCoordinatorMessage, + AvailabilityRecoveryMessage, + ApprovalDistributionMessage, + CandidateValidationMessage, + ])] approval_voting: ApprovalVoting, - #[subsystem(GossipSupportMessage)] + #[subsystem(GossipSupportMessage, sends: [ + NetworkBridgeMessage, + RuntimeApiMessage, + ChainSelectionMessage, + ])] gossip_support: GossipSupport, - #[subsystem(no_dispatch, blocking, DisputeCoordinatorMessage)] + #[subsystem(blocking, DisputeCoordinatorMessage, sends: [ + RuntimeApiMessage, + ChainApiMessage, + DisputeDistributionMessage, + CandidateValidationMessage, + AvailabilityStoreMessage, + AvailabilityRecoveryMessage, + ])] dispute_coordinator: DisputeCoordinator, - #[subsystem(no_dispatch, DisputeDistributionMessage)] + #[subsystem(DisputeDistributionMessage, sends: [ + RuntimeApiMessage, + DisputeCoordinatorMessage, + NetworkBridgeMessage, + ])] dispute_distribution: DisputeDistribution, - #[subsystem(no_dispatch, blocking, ChainSelectionMessage)] + #[subsystem(blocking, ChainSelectionMessage, sends: [ChainApiMessage])] chain_selection: ChainSelection, /// External listeners waiting for a hash to be in the active-leave set. diff --git a/node/overseer/src/tests.rs b/node/overseer/src/tests.rs index 2d486b75d883..9fb030140191 100644 --- a/node/overseer/src/tests.rs +++ b/node/overseer/src/tests.rs @@ -60,11 +60,7 @@ struct TestSubsystem1(metered::MeteredSender); impl overseer::Subsystem for TestSubsystem1 where - C: overseer::SubsystemContext< - Message = CandidateValidationMessage, - Signal = OverseerSignal, - AllMessages = AllMessages, - >, + C: overseer::SubsystemContext, { fn start(self, mut ctx: C) -> SpawnedSubsystem { let mut sender = self.0; @@ -95,8 +91,8 @@ impl overseer::Subsystem for TestSubsystem2 where C: overseer::SubsystemContext< Message = CandidateBackingMessage, + OutgoingMessages = ::OutgoingMessages, Signal = OverseerSignal, - AllMessages = AllMessages, >, { fn start(self, mut ctx: C) -> SpawnedSubsystem { @@ -143,11 +139,7 @@ struct ReturnOnStart; impl overseer::Subsystem for ReturnOnStart where - C: overseer::SubsystemContext< - Message = CandidateBackingMessage, - Signal = OverseerSignal, - AllMessages = AllMessages, - >, + C: overseer::SubsystemContext, { fn start(self, mut _ctx: C) -> SpawnedSubsystem { SpawnedSubsystem { @@ -316,11 +308,7 @@ struct TestSubsystem5(metered::MeteredSender); impl overseer::Subsystem for TestSubsystem5 where - C: overseer::SubsystemContext< - Message = CandidateValidationMessage, - Signal = OverseerSignal, - AllMessages = AllMessages, - >, + C: overseer::SubsystemContext, { fn start(self, mut ctx: C) -> SpawnedSubsystem { let mut sender = self.0.clone(); @@ -352,11 +340,7 @@ struct TestSubsystem6(metered::MeteredSender); impl Subsystem for TestSubsystem6 where - C: overseer::SubsystemContext< - Message = CandidateBackingMessage, - Signal = OverseerSignal, - AllMessages = AllMessages, - >, + C: overseer::SubsystemContext, { fn start(self, mut ctx: C) -> SpawnedSubsystem { let mut sender = self.0.clone(); @@ -761,7 +745,7 @@ impl CounterSubsystem { impl Subsystem for CounterSubsystem where - C: overseer::SubsystemContext, + C: overseer::SubsystemContext, M: Send, { fn start(self, mut ctx: C) -> SpawnedSubsystem { diff --git a/node/primitives/src/lib.rs b/node/primitives/src/lib.rs index 6e28b1d34d9f..c203e560647d 100644 --- a/node/primitives/src/lib.rs +++ b/node/primitives/src/lib.rs @@ -86,7 +86,7 @@ pub const MAX_FINALITY_LAG: u32 = 500; /// We are not using `NonZeroU32` here because `expect` and `unwrap` are not yet const, so global /// constants of `SessionWindowSize` would require `lazy_static` in that case. /// -/// See: https://github.com/rust-lang/rust/issues/67441 +/// See: #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] pub struct SessionWindowSize(SessionIndex); diff --git a/node/service/Cargo.toml b/node/service/Cargo.toml index af50c481c6ff..632dc429f7fe 100644 --- a/node/service/Cargo.toml +++ b/node/service/Cargo.toml @@ -83,6 +83,7 @@ polkadot-node-primitives = { path = "../primitives" } polkadot-rpc = { path = "../../rpc" } polkadot-node-subsystem = {path = "../subsystem" } polkadot-node-subsystem-util = { path = "../subsystem-util" } +polkadot-node-subsystem-types = { path = "../subsystem-types" } polkadot-runtime-parachains = { path = "../../runtime/parachains" } polkadot-node-network-protocol = { path = "../network/protocol" } diff --git a/node/service/src/overseer.rs b/node/service/src/overseer.rs index aa90abb91fb2..bb3d9e840f1c 100644 --- a/node/service/src/overseer.rs +++ b/node/service/src/overseer.rs @@ -24,14 +24,16 @@ use polkadot_node_core_chain_selection::Config as ChainSelectionConfig; use polkadot_node_core_dispute_coordinator::Config as DisputeCoordinatorConfig; use polkadot_node_core_provisioner::ProvisionerConfig; use polkadot_node_network_protocol::request_response::{v1 as request_v1, IncomingRequestReceiver}; +use polkadot_node_subsystem_types::messages::{BitfieldSigningMessage, ProvisionerMessage}; #[cfg(any(feature = "malus", test))] pub use polkadot_overseer::{ dummy::{dummy_overseer_builder, DummySubsystem}, HeadSupportsParachains, }; use polkadot_overseer::{ - metrics::Metrics as OverseerMetrics, BlockInfo, InitializedOverseerBuilder, MetricsTrait, - Overseer, OverseerConnector, OverseerHandle, + gen::SubsystemContext, metrics::Metrics as OverseerMetrics, BlockInfo, + InitializedOverseerBuilder, MetricsTrait, Overseer, OverseerConnector, OverseerHandle, + OverseerSubsystemContext, }; use polkadot_primitives::runtime_api::ParachainHost; @@ -152,9 +154,15 @@ pub fn prepared_overseer_builder<'a, Spawner, RuntimeClient>( StatementDistributionSubsystem, AvailabilityDistributionSubsystem, AvailabilityRecoverySubsystem, - BitfieldSigningSubsystem, + BitfieldSigningSubsystem< + Spawner, + as SubsystemContext>::Sender, + >, BitfieldDistributionSubsystem, - ProvisionerSubsystem, + ProvisionerSubsystem< + Spawner, + as SubsystemContext>::Sender, + >, RuntimeApiSubsystem, AvailabilityStoreSubsystem, NetworkBridgeSubsystem< diff --git a/node/subsystem-test-helpers/src/lib.rs b/node/subsystem-test-helpers/src/lib.rs index 06249e109d06..b36c180a57b7 100644 --- a/node/subsystem-test-helpers/src/lib.rs +++ b/node/subsystem-test-helpers/src/lib.rs @@ -20,7 +20,7 @@ use polkadot_node_subsystem::{ messages::AllMessages, overseer, FromOverseer, OverseerSignal, SpawnedSubsystem, - SubsystemContext, SubsystemError, SubsystemResult, + SubsystemError, SubsystemResult, }; use polkadot_node_subsystem_util::TimeoutExt; @@ -150,24 +150,25 @@ pub fn sender_receiver() -> (TestSubsystemSender, mpsc::UnboundedReceiver overseer::SubsystemSender for TestSubsystemSender +impl overseer::SubsystemSender for TestSubsystemSender where - T: Into + Send + 'static, + AllMessages: From, + OutgoingMessage: Send + 'static, { - async fn send_message(&mut self, msg: T) { + async fn send_message(&mut self, msg: OutgoingMessage) { self.tx.send(msg.into()).await.expect("test overseer no longer live"); } - async fn send_messages(&mut self, msgs: X) + async fn send_messages(&mut self, msgs: I) where - X: IntoIterator + Send, - X::IntoIter: Send, + I: IntoIterator + Send, + I::IntoIter: Send, { let mut iter = stream::iter(msgs.into_iter().map(|msg| Ok(msg.into()))); self.tx.send_all(&mut iter).await.expect("test overseer no longer live"); } - fn send_unbounded_message(&mut self, msg: T) { + fn send_unbounded_message(&mut self, msg: OutgoingMessage) { self.tx.unbounded_send(msg.into()).expect("test overseer no longer live"); } } @@ -180,16 +181,17 @@ pub struct TestSubsystemContext { } #[async_trait::async_trait] -impl overseer::SubsystemContext for TestSubsystemContext +impl overseer::SubsystemContext for TestSubsystemContext where - M: std::fmt::Debug + Send + 'static, + M: overseer::AssociateOutgoing + std::fmt::Debug + Send + 'static, + AllMessages: From<::OutgoingMessages>, AllMessages: From, - S: SpawnNamed + Send + 'static, + Spawner: SpawnNamed + Send + 'static, { type Message = M; type Sender = TestSubsystemSender; type Signal = OverseerSignal; - type AllMessages = AllMessages; + type OutgoingMessages = ::OutgoingMessages; type Error = SubsystemError; async fn try_recv(&mut self) -> Result>, ()> { @@ -316,8 +318,13 @@ pub struct ForwardSubsystem(pub mpsc::Sender); impl overseer::Subsystem for ForwardSubsystem where - M: std::fmt::Debug + Send + 'static, - Context: SubsystemContext + overseer::SubsystemContext, + M: overseer::AssociateOutgoing + std::fmt::Debug + Send + 'static, + Context: overseer::SubsystemContext< + Message = M, + Signal = OverseerSignal, + Error = SubsystemError, + OutgoingMessages = ::OutgoingMessages, + >, { fn start(mut self, mut ctx: Context) -> SpawnedSubsystem { let future = Box::pin(async move { diff --git a/node/subsystem-util/src/determine_new_blocks.rs b/node/subsystem-util/src/determine_new_blocks.rs index 3c862b1fc704..c2b54160cc86 100644 --- a/node/subsystem-util/src/determine_new_blocks.rs +++ b/node/subsystem-util/src/determine_new_blocks.rs @@ -39,7 +39,7 @@ pub async fn determine_new_blocks( lower_bound_number: BlockNumber, ) -> Result, E> where - Sender: SubsystemSender, + Sender: SubsystemSender, { const ANCESTRY_STEP: usize = 4; diff --git a/node/subsystem-util/src/lib.rs b/node/subsystem-util/src/lib.rs index b249fa11f0eb..dcc7c5615431 100644 --- a/node/subsystem-util/src/lib.rs +++ b/node/subsystem-util/src/lib.rs @@ -26,9 +26,7 @@ use polkadot_node_subsystem::{ errors::{RuntimeApiError, SubsystemError}, - messages::{ - AllMessages, BoundToRelayParent, RuntimeApiMessage, RuntimeApiRequest, RuntimeApiSender, - }, + messages::{BoundToRelayParent, RuntimeApiMessage, RuntimeApiRequest, RuntimeApiSender}, overseer, ActivatedLeaf, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemContext, SubsystemSender, }; @@ -144,7 +142,7 @@ pub async fn request_from_runtime( ) -> RuntimeApiReceiver where RequestBuilder: FnOnce(RuntimeApiSender) -> RuntimeApiRequest, - Sender: SubsystemSender, + Sender: SubsystemSender, { let (tx, rx) = oneshot::channel(); @@ -176,7 +174,7 @@ macro_rules! specialize_requests { $( $param_name: $param_ty, )* - sender: &mut impl SubsystemSender, + sender: &mut impl overseer::SubsystemSender, ) -> RuntimeApiReceiver<$return_ty> { request_from_runtime(parent, sender, |tx| RuntimeApiRequest::$request_variant( @@ -329,11 +327,14 @@ pub struct Validator { impl Validator { /// Get a struct representing this node's validator if this node is in fact a validator in the context of the given block. - pub async fn new( + pub async fn new( parent: Hash, keystore: SyncCryptoStorePtr, - sender: &mut impl SubsystemSender, - ) -> Result { + sender: &mut S, + ) -> Result + where + S: SubsystemSender, + { // Note: request_validators and request_session_index_for_child do not and cannot // run concurrently: they both have a mutable handle to the same sender. // However, each of them returns a oneshot::Receiver, and those are resolved concurrently. @@ -397,14 +398,14 @@ impl Drop for AbortOnDrop { } /// A `JobHandle` manages a particular job for a subsystem. -struct JobHandle { +struct JobHandle { _abort_handle: AbortOnDrop, - to_job: mpsc::Sender, + to_job: mpsc::Sender, } -impl JobHandle { +impl JobHandle { /// Send a message to the job. - async fn send_msg(&mut self, msg: ToJob) -> Result<(), Error> { + async fn send_msg(&mut self, msg: Consumes) -> Result<(), Error> { self.to_job.send(msg).await.map_err(Into::into) } } @@ -418,49 +419,25 @@ pub enum FromJobCommand { } /// A sender for messages from jobs, as well as commands to the overseer. -pub struct JobSender { +pub struct JobSender { sender: S, from_job: mpsc::Sender, } // A custom clone impl, since M does not need to impl `Clone` // which `#[derive(Clone)]` requires. -impl Clone for JobSender { +impl Clone for JobSender { fn clone(&self) -> Self { Self { sender: self.sender.clone(), from_job: self.from_job.clone() } } } -impl JobSender { +impl JobSender { /// Get access to the underlying subsystem sender. pub fn subsystem_sender(&mut self) -> &mut S { &mut self.sender } - /// Send a direct message to some other `Subsystem`, routed based on message type. - pub async fn send_message(&mut self, msg: impl Into) { - self.sender.send_message(msg.into()).await - } - - /// Send multiple direct messages to other `Subsystem`s, routed based on message type. - pub async fn send_messages(&mut self, msgs: T) - where - T: IntoIterator + Send, - T::IntoIter: Send, - M: Into, - { - self.sender.send_messages(msgs.into_iter().map(|m| m.into())).await - } - - /// Send a message onto the unbounded queue of some other `Subsystem`, routed based on message - /// type. - /// - /// This function should be used only when there is some other bounding factor on the messages - /// sent with it. Otherwise, it risks a memory leak. - pub fn send_unbounded_message(&mut self, msg: impl Into) { - self.sender.send_unbounded_message(msg.into()) - } - /// Send a command to the subsystem, to be relayed onwards to the overseer. pub async fn send_command(&mut self, msg: FromJobCommand) -> Result<(), mpsc::SendError> { self.from_job.send(msg).await @@ -470,23 +447,23 @@ impl JobSender { #[async_trait::async_trait] impl overseer::SubsystemSender for JobSender where - M: Send + 'static + Into, - S: SubsystemSender + Clone, + M: Send + 'static, + S: SubsystemSender + Clone, { async fn send_message(&mut self, msg: M) { - self.sender.send_message(msg.into()).await + self.sender.send_message(msg).await } - async fn send_messages(&mut self, msgs: T) + async fn send_messages(&mut self, msgs: I) where - T: IntoIterator + Send, - T::IntoIter: Send, + I: IntoIterator + Send, + I::IntoIter: Send, { - self.sender.send_messages(msgs.into_iter().map(|m| m.into())).await + self.sender.send_messages(msgs).await } fn send_unbounded_message(&mut self, msg: M) { - self.sender.send_unbounded_message(msg.into()) + self.sender.send_unbounded_message(msg) } } @@ -506,6 +483,14 @@ impl fmt::Debug for FromJobCommand { pub trait JobTrait: Unpin + Sized { /// Message type used to send messages to the job. type ToJob: 'static + BoundToRelayParent + Send; + + /// The set of outgoing messages to be accumulated into. + type OutgoingMessages: 'static + Send; + + /// The sender to send outgoing messages. + // The trait bounds are rather minimal. + type Sender: 'static + Send + Clone; + /// Job runtime error. type Error: 'static + std::error::Error + Send; /// Extra arguments this job needs to run properly. @@ -525,12 +510,12 @@ pub trait JobTrait: Unpin + Sized { /// Run a job for the given relay `parent`. /// /// The job should be ended when `receiver` returns `None`. - fn run( + fn run( leaf: ActivatedLeaf, run_args: Self::RunArgs, metrics: Self::Metrics, receiver: mpsc::Receiver, - sender: JobSender, + sender: JobSender, ) -> Pin> + Send>>; } @@ -572,15 +557,14 @@ where } /// Spawn a new job for this `parent_hash`, with whatever args are appropriate. - fn spawn_job( + fn spawn_job( &mut self, leaf: ActivatedLeaf, run_args: Job::RunArgs, metrics: Job::Metrics, - sender: Sender, + sender: Job::Sender, ) where Job: JobTrait, - Sender: SubsystemSender, { let hash = leaf.hash; let (to_job_tx, to_job_rx) = mpsc::channel(JOB_CHANNEL_CAPACITY); @@ -697,8 +681,12 @@ impl JobSubsystem { pub async fn run(self, mut ctx: Context) where Spawner: SpawnNamed + Send + Clone + Unpin + 'static, - Context: SubsystemContext::ToJob, Signal = OverseerSignal>, - ::Sender: SubsystemSender, + Context: SubsystemContext< + Message = ::ToJob, + OutgoingMessages = ::OutgoingMessages, + Sender = ::Sender, + Signal = OverseerSignal, + >, Job: 'static + JobTrait + Send, ::RunArgs: Clone + Sync, ::ToJob: @@ -719,7 +707,7 @@ impl JobSubsystem { }))) => { for activated in activated { let sender = ctx.sender().clone(); - jobs.spawn_job::( + jobs.spawn_job::( activated, run_args.clone(), metrics.clone(), @@ -773,11 +761,15 @@ impl JobSubsystem { impl Subsystem for JobSubsystem where Spawner: SpawnNamed + Send + Clone + Unpin + 'static, - Context: SubsystemContext, + Context: SubsystemContext< + Message = Job::ToJob, + Signal = OverseerSignal, + OutgoingMessages = ::OutgoingMessages, + Sender = ::Sender, + >, Job: 'static + JobTrait + Send, Job::RunArgs: Clone + Sync, - ::ToJob: - Sync + From<::Message>, + ::ToJob: Sync + From<::Message>, Job::Metrics: Sync, { fn start(self, ctx: Context) -> SpawnedSubsystem { diff --git a/node/subsystem-util/src/rolling_session_window.rs b/node/subsystem-util/src/rolling_session_window.rs index c8a467d47d38..ba8a62d08118 100644 --- a/node/subsystem-util/src/rolling_session_window.rs +++ b/node/subsystem-util/src/rolling_session_window.rs @@ -26,7 +26,7 @@ use futures::channel::oneshot; use polkadot_node_subsystem::{ errors::RuntimeApiError, messages::{RuntimeApiMessage, RuntimeApiRequest}, - overseer, SubsystemContext, + overseer, }; use thiserror::Error; @@ -94,16 +94,19 @@ pub struct RollingSessionWindow { impl RollingSessionWindow { /// Initialize a new session info cache with the given window size. - pub async fn new( - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + pub async fn new( + mut sender: Sender, window_size: SessionWindowSize, block_hash: Hash, - ) -> Result { - let session_index = get_session_index_for_child(ctx, block_hash).await?; + ) -> Result + where + Sender: overseer::SubsystemSender, + { + let session_index = get_session_index_for_child(&mut sender, block_hash).await?; let window_start = session_index.saturating_sub(window_size.get() - 1); - match load_all_sessions(ctx, block_hash, window_start, session_index).await { + match load_all_sessions(&mut sender, block_hash, window_start, session_index).await { Err(kind) => Err(SessionsUnavailable { kind, info: Some(SessionsUnavailableInfo { @@ -154,10 +157,10 @@ impl RollingSessionWindow { /// some backwards drift in session index is acceptable. pub async fn cache_session_info_for_head( &mut self, - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + sender: &mut impl overseer::SubsystemSender, block_hash: Hash, ) -> Result { - let session_index = get_session_index_for_child(ctx, block_hash).await?; + let session_index = get_session_index_for_child(sender, block_hash).await?; let old_window_start = self.earliest_session; @@ -177,7 +180,7 @@ impl RollingSessionWindow { let fresh_start = if latest < window_start { window_start } else { latest + 1 }; - match load_all_sessions(ctx, block_hash, fresh_start, session_index).await { + match load_all_sessions(sender, block_hash, fresh_start, session_index).await { Err(kind) => Err(SessionsUnavailable { kind, info: Some(SessionsUnavailableInfo { @@ -215,17 +218,18 @@ impl RollingSessionWindow { // cleaner to just call the runtime API directly without needing to create an instance // of `RuntimeInfo`. async fn get_session_index_for_child( - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + sender: &mut impl overseer::SubsystemSender, block_hash: Hash, ) -> Result { let (s_tx, s_rx) = oneshot::channel(); // We're requesting session index of a child to populate the cache in advance. - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::SessionIndexForChild(s_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::SessionIndexForChild(s_tx), + )) + .await; match s_rx.await { Ok(Ok(s)) => Ok(s), @@ -243,7 +247,7 @@ async fn get_session_index_for_child( } async fn load_all_sessions( - ctx: &mut (impl SubsystemContext + overseer::SubsystemContext), + sender: &mut impl overseer::SubsystemSender, block_hash: Hash, start: SessionIndex, end_inclusive: SessionIndex, @@ -251,11 +255,12 @@ async fn load_all_sessions( let mut v = Vec::new(); for i in start..=end_inclusive { let (tx, rx) = oneshot::channel(); - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::SessionInfo(i, tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::SessionInfo(i, tx), + )) + .await; let session_info = match rx.await { Ok(Ok(Some(s))) => s, @@ -274,7 +279,10 @@ async fn load_all_sessions( mod tests { use super::*; use assert_matches::assert_matches; - use polkadot_node_subsystem::messages::{AllMessages, AvailabilityRecoveryMessage}; + use polkadot_node_subsystem::{ + messages::{AllMessages, AvailabilityRecoveryMessage}, + SubsystemContext, + }; use polkadot_node_subsystem_test_helpers::make_subsystem_context; use polkadot_primitives::v2::Header; use sp_core::testing::TaskExecutor; @@ -319,13 +327,16 @@ mod tests { let hash = header.hash(); + let sender = ctx.sender(); + let test_fut = { Box::pin(async move { let window = match window { - None => - RollingSessionWindow::new(&mut ctx, TEST_WINDOW_SIZE, hash).await.unwrap(), + None => RollingSessionWindow::new(sender.clone(), TEST_WINDOW_SIZE, hash) + .await + .unwrap(), Some(mut window) => { - window.cache_session_info_for_head(&mut ctx, hash).await.unwrap(); + window.cache_session_info_for_head(sender, hash).await.unwrap(); window }, }; @@ -495,8 +506,9 @@ mod tests { let hash = header.hash(); let test_fut = { + let sender = ctx.sender().clone(); Box::pin(async move { - let res = RollingSessionWindow::new(&mut ctx, TEST_WINDOW_SIZE, hash).await; + let res = RollingSessionWindow::new(sender, TEST_WINDOW_SIZE, hash).await; assert!(res.is_err()); }) }; @@ -555,8 +567,9 @@ mod tests { let test_fut = { Box::pin(async move { + let sender = ctx.sender().clone(); let window = - RollingSessionWindow::new(&mut ctx, TEST_WINDOW_SIZE, hash).await.unwrap(); + RollingSessionWindow::new(sender, TEST_WINDOW_SIZE, hash).await.unwrap(); assert_eq!(window.earliest_session, session); assert_eq!(window.session_info, vec![dummy_session_info(session)]); diff --git a/node/subsystem-util/src/runtime/mod.rs b/node/subsystem-util/src/runtime/mod.rs index 4286eba89a54..fc660a9dc6df 100644 --- a/node/subsystem-util/src/runtime/mod.rs +++ b/node/subsystem-util/src/runtime/mod.rs @@ -25,7 +25,7 @@ use sp_application_crypto::AppKey; use sp_core::crypto::ByteArray; use sp_keystore::{CryptoStore, SyncCryptoStorePtr}; -use polkadot_node_subsystem::{SubsystemContext, SubsystemSender}; +use polkadot_node_subsystem::{messages::RuntimeApiMessage, overseer, SubsystemSender}; use polkadot_primitives::v2::{ CandidateEvent, CoreState, EncodeAs, GroupIndex, GroupRotationInfo, Hash, OccupiedCore, ScrapedOnChainVotes, SessionIndex, SessionInfo, Signed, SigningContext, UncheckedSigned, @@ -123,7 +123,7 @@ impl RuntimeInfo { parent: Hash, ) -> Result where - Sender: SubsystemSender, + Sender: SubsystemSender, { match self.session_index_cache.get(&parent) { Some(index) => Ok(*index), @@ -143,7 +143,7 @@ impl RuntimeInfo { relay_parent: Hash, ) -> Result<&'a ExtendedSessionInfo> where - Sender: SubsystemSender, + Sender: SubsystemSender, { let session_index = self.get_session_index_for_child(sender, relay_parent).await?; @@ -161,7 +161,7 @@ impl RuntimeInfo { session_index: SessionIndex, ) -> Result<&'a ExtendedSessionInfo> where - Sender: SubsystemSender, + Sender: SubsystemSender, { if !self.session_info_cache.contains(&session_index) { let session_info = @@ -190,7 +190,7 @@ impl RuntimeInfo { std::result::Result, UncheckedSigned>, > where - Sender: SubsystemSender, + Sender: SubsystemSender, Payload: EncodeAs + Clone, RealPayload: Encode + Clone, { @@ -257,25 +257,25 @@ where } /// Request availability cores from the runtime. -pub async fn get_availability_cores( - ctx: &mut Context, +pub async fn get_availability_cores( + sender: &mut Sender, relay_parent: Hash, ) -> Result> where - Context: SubsystemContext, + Sender: overseer::SubsystemSender, { - recv_runtime(request_availability_cores(relay_parent, ctx.sender()).await).await + recv_runtime(request_availability_cores(relay_parent, sender).await).await } /// Variant of `request_availability_cores` that only returns occupied ones. -pub async fn get_occupied_cores( - ctx: &mut Context, +pub async fn get_occupied_cores( + sender: &mut Sender, relay_parent: Hash, ) -> Result> where - Context: SubsystemContext, + Sender: overseer::SubsystemSender, { - let cores = get_availability_cores(ctx, relay_parent).await?; + let cores = get_availability_cores(sender, relay_parent).await?; Ok(cores .into_iter() @@ -290,17 +290,16 @@ where } /// Get group rotation info based on the given `relay_parent`. -pub async fn get_group_rotation_info( - ctx: &mut Context, +pub async fn get_group_rotation_info( + sender: &mut Sender, relay_parent: Hash, ) -> Result where - Context: SubsystemContext, + Sender: overseer::SubsystemSender, { // We drop `groups` here as we don't need them, because of `RuntimeInfo`. Ideally we would not // fetch them in the first place. - let (_, info) = - recv_runtime(request_validator_groups(relay_parent, ctx.sender()).await).await?; + let (_, info) = recv_runtime(request_validator_groups(relay_parent, sender).await).await?; Ok(info) } @@ -310,7 +309,7 @@ pub async fn get_candidate_events( relay_parent: Hash, ) -> Result> where - Sender: SubsystemSender, + Sender: SubsystemSender, { recv_runtime(request_candidate_events(relay_parent, sender).await).await } @@ -321,7 +320,7 @@ pub async fn get_on_chain_votes( relay_parent: Hash, ) -> Result> where - Sender: SubsystemSender, + Sender: SubsystemSender, { recv_runtime(request_on_chain_votes(relay_parent, sender).await).await } @@ -333,7 +332,7 @@ pub async fn get_validation_code_by_hash( validation_code_hash: ValidationCodeHash, ) -> Result> where - Sender: SubsystemSender, + Sender: SubsystemSender, { recv_runtime(request_validation_code_by_hash(relay_parent, validation_code_hash, sender).await) .await diff --git a/node/subsystem-util/src/tests.rs b/node/subsystem-util/src/tests.rs index 166b4d557508..2a1a95a88458 100644 --- a/node/subsystem-util/src/tests.rs +++ b/node/subsystem-util/src/tests.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . +#![cfg(test)] + use super::*; use assert_matches::assert_matches; use executor::block_on; @@ -44,8 +46,9 @@ use thiserror::Error; // job structs are constructed within JobTrait::run // most will want to retain the sender and receiver, as well as whatever other data they like -struct FakeCollatorProtocolJob { +struct FakeCollatorProtocolJob { receiver: mpsc::Receiver, + _phantom: std::marker::PhantomData, } // Error will mostly be a wrapper to make the try operator more convenient; @@ -57,8 +60,18 @@ enum Error { Sending(#[from] mpsc::SendError), } -impl JobTrait for FakeCollatorProtocolJob { +impl JobTrait for FakeCollatorProtocolJob +where + Sender: overseer::CollatorProtocolSenderTrait + + std::marker::Unpin + + overseer::SubsystemSender, + JobSender: overseer::CollatorProtocolSenderTrait + + std::marker::Unpin + + overseer::SubsystemSender, +{ type ToJob = CollatorProtocolMessage; + type OutgoingMessages = overseer::CollatorProtocolOutgoingMessages; + type Sender = Sender; type Error = Error; type RunArgs = bool; type Metrics = (); @@ -68,20 +81,21 @@ impl JobTrait for FakeCollatorProtocolJob { /// Run a job for the parent block indicated // // this function is in charge of creating and executing the job's main loop - fn run( + fn run( _: ActivatedLeaf, run_args: Self::RunArgs, _metrics: Self::Metrics, receiver: mpsc::Receiver, - mut sender: JobSender, + mut sender: JobSender, ) -> Pin> + Send>> { async move { - let job = FakeCollatorProtocolJob { receiver }; + let job = + FakeCollatorProtocolJob { receiver, _phantom: std::marker::PhantomData:: }; if run_args { sender .send_message(CollatorProtocolMessage::Invalid( - Default::default(), + dummy_hash(), dummy_candidate_receipt(dummy_hash()), )) .await; @@ -95,7 +109,10 @@ impl JobTrait for FakeCollatorProtocolJob { } } -impl FakeCollatorProtocolJob { +impl FakeCollatorProtocolJob +where + Sender: overseer::CollatorProtocolSenderTrait, +{ async fn run_loop(mut self) -> Result<(), Error> { loop { match self.receiver.next().await { @@ -111,7 +128,8 @@ impl FakeCollatorProtocolJob { } // with the job defined, it's straightforward to get a subsystem implementation. -type FakeCollatorProtocolSubsystem = JobSubsystem; +type FakeCollatorProtocolSubsystem = + JobSubsystem, Spawner>; // this type lets us pretend to be the overseer type OverseerHandle = test_helpers::TestSubsystemContextHandle; diff --git a/node/subsystem/src/lib.rs b/node/subsystem/src/lib.rs index 054f0d5997bb..2ffd4871f4f8 100644 --- a/node/subsystem/src/lib.rs +++ b/node/subsystem/src/lib.rs @@ -24,9 +24,7 @@ pub use jaeger::*; pub use polkadot_node_jaeger as jaeger; -pub use polkadot_overseer::{ - self as overseer, ActiveLeavesUpdate, OverseerConnector, OverseerSignal, -}; +pub use polkadot_overseer::{self as overseer, *}; pub use polkadot_node_subsystem_types::{ errors::{self, *}, @@ -54,37 +52,5 @@ pub type FromOverseer = polkadot_overseer::gen::FromOverseer = polkadot_overseer::gen::SubsystemInstance; -/// Sender trait for the `AllMessages` wrapper. -pub trait SubsystemSender: polkadot_overseer::gen::SubsystemSender {} - -impl SubsystemSender for T where T: polkadot_overseer::gen::SubsystemSender -{} - /// Spawned subsystem. pub type SpawnedSubsystem = polkadot_overseer::gen::SpawnedSubsystem; - -/// Convenience trait specialization. -pub trait SubsystemContext: - polkadot_overseer::gen::SubsystemContext< - Signal = OverseerSignal, - AllMessages = messages::AllMessages, - Error = SubsystemError, -> -{ - /// The message type the subsystem consumes. - type Message: std::fmt::Debug + Send + 'static; - /// Sender type to communicate with other subsystems. - type Sender: SubsystemSender + Send + Clone + 'static; -} - -impl SubsystemContext for T -where - T: polkadot_overseer::gen::SubsystemContext< - Signal = OverseerSignal, - AllMessages = messages::AllMessages, - Error = SubsystemError, - >, -{ - type Message = ::Message; - type Sender = ::Sender; -} diff --git a/parachain/src/primitives.rs b/parachain/src/primitives.rs index b7de1d0ada33..54c67cdf1a05 100644 --- a/parachain/src/primitives.rs +++ b/parachain/src/primitives.rs @@ -65,7 +65,7 @@ impl ValidationCode { } } -/// Unit type wrapper around [`Hash`] that represents a validation code hash. +/// Unit type wrapper around [`type@Hash`] that represents a validation code hash. /// /// This type is produced by [`ValidationCode::hash`]. /// diff --git a/utils/staking-miner/src/rpc.rs b/utils/staking-miner/src/rpc.rs index a4a366dc2a54..b609e197b49d 100644 --- a/utils/staking-miner/src/rpc.rs +++ b/utils/staking-miner/src/rpc.rs @@ -64,7 +64,7 @@ pub trait RpcApi { at: Option<&Hash>, ) -> RpcResult>; - /// Dry run an extrinsic at a given block. Return SCALE encoded [`sp_runtine::ApplyExtrinsicResult`]. + /// Dry run an extrinsic at a given block. Return SCALE encoded [`sp_runtime::ApplyExtrinsicResult`]. #[method(name = "system_dryRun")] async fn dry_run(&self, extrinsic: &Bytes, at: Option) -> RpcResult;