diff --git a/substrate/bin/node-template/node/src/service.rs b/substrate/bin/node-template/node/src/service.rs index 339aafbefcdf4775a234b21accb8e5feff1b256c..021d0ac8f7d1e74f8d95d200f4b6a672fbdb4d81 100644 --- a/substrate/bin/node-template/node/src/service.rs +++ b/substrate/bin/node-template/node/src/service.rs @@ -83,7 +83,7 @@ pub fn new_full(config: Configuration) -> Result<TaskManager, ServiceError> { let finality_proof_provider = GrandpaFinalityProofProvider::new_for_service(backend.clone(), client.clone()); - let (network, network_status_sinks, system_rpc_tx) = + let (network, network_status_sinks, system_rpc_tx, network_starter) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, client: client.clone(), @@ -215,6 +215,7 @@ pub fn new_full(config: Configuration) -> Result<TaskManager, ServiceError> { )?; } + network_starter.start_network(); Ok(task_manager) } @@ -253,7 +254,7 @@ pub fn new_light(config: Configuration) -> Result<TaskManager, ServiceError> { let finality_proof_provider = GrandpaFinalityProofProvider::new_for_service(backend.clone(), client.clone()); - let (network, network_status_sinks, system_rpc_tx) = + let (network, network_status_sinks, system_rpc_tx, network_starter) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, client: client.clone(), @@ -288,5 +289,7 @@ pub fn new_light(config: Configuration) -> Result<TaskManager, ServiceError> { system_rpc_tx, })?; + network_starter.start_network(); + Ok(task_manager) } diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index a47869ed83232bed0fb5c51bba0263a5ab90bd96..fdfa6816296aaccfc2676383e250ac4b5aeb60d2 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -164,8 +164,8 @@ pub fn new_full_base( let finality_proof_provider = GrandpaFinalityProofProvider::new_for_service(backend.clone(), client.clone()); - - let (network, network_status_sinks, system_rpc_tx) = + + let (network, network_status_sinks, system_rpc_tx, network_starter) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, client: client.clone(), @@ -206,7 +206,7 @@ pub fn new_full_base( network_status_sinks, system_rpc_tx, })?; - + let (block_import, grandpa_link, babe_link) = import_setup; let shared_voter_state = rpc_setup; @@ -322,6 +322,7 @@ pub fn new_full_base( )?; } + network_starter.start_network(); Ok((task_manager, inherent_data_providers, client, network, transaction_pool)) } @@ -383,7 +384,7 @@ pub fn new_light_base(config: Configuration) -> Result<( let finality_proof_provider = GrandpaFinalityProofProvider::new_for_service(backend.clone(), client.clone()); - let (network, network_status_sinks, system_rpc_tx) = + let (network, network_status_sinks, system_rpc_tx, network_starter) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, client: client.clone(), @@ -395,7 +396,8 @@ pub fn new_light_base(config: Configuration) -> Result<( finality_proof_request_builder: Some(finality_proof_request_builder), finality_proof_provider: Some(finality_proof_provider), })?; - + network_starter.start_network(); + if config.offchain_worker.enabled { sc_service::build_offchain_workers( &config, backend.clone(), task_manager.spawn_handle(), client.clone(), network.clone(), @@ -412,7 +414,7 @@ pub fn new_light_base(config: Configuration) -> Result<( let rpc_extensions = node_rpc::create_light(light_deps); let rpc_handlers = - sc_service::spawn_tasks(sc_service::SpawnTasksParams { + sc_service::spawn_tasks(sc_service::SpawnTasksParams { on_demand: Some(on_demand), remote_blockchain: Some(backend.remote_blockchain()), rpc_extensions_builder: Box::new(sc_service::NoopRpcExtensionBuilder(rpc_extensions)), @@ -423,7 +425,7 @@ pub fn new_light_base(config: Configuration) -> Result<( telemetry_connection_sinks: sc_service::TelemetryConnectionSinks::default(), task_manager: &mut task_manager, })?; - + Ok((task_manager, rpc_handlers, client, network, transaction_pool)) } @@ -498,7 +500,7 @@ mod tests { setup_handles = Some((block_import.clone(), babe_link.clone())); } )?; - + let node = sc_service_test::TestNetComponents::new( keep_alive, client, network, transaction_pool ); diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index 4c7c1f57ee04bbfd3b32e48445bf7a53f85ae153..fc9303498d674edf228e1e554c07347f40618935 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -33,7 +33,7 @@ use sp_consensus::{ block_validation::{BlockAnnounceValidator, DefaultBlockAnnounceValidator, Chain}, import_queue::ImportQueue, }; -use futures::{FutureExt, StreamExt, future::ready}; +use futures::{FutureExt, StreamExt, future::ready, channel::oneshot}; use jsonrpc_pubsub::manager::SubscriptionManager; use sc_keystore::Store as Keystore; use log::{info, warn, error}; @@ -668,7 +668,7 @@ fn build_telemetry<TBl: BlockT>( let startup_time = SystemTime::UNIX_EPOCH.elapsed() .map(|dur| dur.as_millis()) .unwrap_or(0); - + spawn_handle.spawn( "telemetry-worker", telemetry.clone() @@ -822,6 +822,7 @@ pub fn build_network<TBl, TExPool, TImpQu, TCl>( Arc<NetworkService<TBl, <TBl as BlockT>::Hash>>, NetworkStatusSinks<TBl>, TracingUnboundedSender<sc_rpc::system::Request<TBl>>, + NetworkStarter, ), Error > @@ -900,6 +901,22 @@ pub fn build_network<TBl, TExPool, TImpQu, TCl>( config.announce_block, ); + // TODO: Normally, one is supposed to pass a list of notifications protocols supported by the + // node through the `NetworkConfiguration` struct. But because this function doesn't know in + // advance which components, such as GrandPa or Polkadot, will be plugged on top of the + // service, it is unfortunately not possible to do so without some deep refactoring. To bypass + // this problem, the `NetworkService` provides a `register_notifications_protocol` method that + // can be called even after the network has been initialized. However, we want to avoid the + // situation where `register_notifications_protocol` is called *after* the network actually + // connects to other peers. For this reason, we delay the process of the network future until + // the user calls `NetworkStarter::start_network`. + // + // This entire hack should eventually be removed in favour of passing the list of protocols + // through the configuration. + // + // See also https://github.com/paritytech/substrate/issues/6827 + let (network_start_tx, network_start_rx) = oneshot::channel(); + // The network worker is responsible for gathering all network messages and processing // them. This is quite a heavy task, and at the time of the writing of this comment it // frequently happens that this future takes several seconds or in some situations @@ -907,7 +924,32 @@ pub fn build_network<TBl, TExPool, TImpQu, TCl>( // issue, and ideally we would like to fix the network future to take as little time as // possible, but we also take the extra harm-prevention measure to execute the networking // future using `spawn_blocking`. - spawn_handle.spawn_blocking("network-worker", future); + spawn_handle.spawn_blocking("network-worker", async move { + if network_start_rx.await.is_err() { + debug_assert!(false); + log::warn!( + "The NetworkStart returned as part of `build_network` has been silently dropped" + ); + // This `return` might seem unnecessary, but we don't want to make it look like + // everything is working as normal even though the user is clearly misusing the API. + return; + } + + future.await + }); + + Ok((network, network_status_sinks, system_rpc_tx, NetworkStarter(network_start_tx))) +} - Ok((network, network_status_sinks, system_rpc_tx)) +/// Object used to start the network. +#[must_use] +pub struct NetworkStarter(oneshot::Sender<()>); + +impl NetworkStarter { + /// Start the network. Call this after all sub-components have been initialized. + /// + /// > **Note**: If you don't call this function, the networking will not work. + pub fn start_network(self) { + let _ = self.0.send(()); + } } diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs index 40826a70d45015454e4f1f2bba9dfd8ca4405f3e..415a5de4f930da5cae93156a8bab663636f67bcf 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -53,7 +53,7 @@ use sp_utils::{status_sinks, mpsc::{tracing_unbounded, TracingUnboundedReceiver, pub use self::error::Error; pub use self::builder::{ new_full_client, new_client, new_full_parts, new_light_parts, - spawn_tasks, build_network, BuildNetworkParams, build_offchain_workers, + spawn_tasks, build_network, BuildNetworkParams, NetworkStarter, build_offchain_workers, SpawnTasksParams, TFullClient, TLightClient, TFullBackend, TLightBackend, TLightBackendWithHash, TLightClientWithBackend, TFullCallExecutor, TLightCallExecutor, RpcExtensionBuilder, NoopRpcExtensionBuilder,