referrerpolicy=no-referrer-when-downgrade

cumulus_relay_chain_minimal_node/
network.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
5// Cumulus is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9
10// Cumulus is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14
15// You should have received a copy of the GNU General Public License
16// along with Cumulus. If not, see <https://www.gnu.org/licenses/>.
17
18use polkadot_core_primitives::{Block, Hash, Header};
19use sp_runtime::traits::NumberFor;
20
21use sc_network::{
22	config::{
23		NetworkConfiguration, NonReservedPeerMode, NotificationHandshake, PeerStore, ProtocolId,
24		SetConfig,
25	},
26	peer_store::PeerStoreProvider,
27	service::traits::NetworkService,
28	NotificationMetrics,
29};
30
31use sc_network::{config::FullNetworkConfiguration, NetworkBackend, NotificationService};
32use sc_network_common::{role::Roles, sync::message::BlockAnnouncesHandshake};
33use sc_service::{error::Error, Configuration, SpawnTaskHandle};
34
35use std::{iter, sync::Arc};
36
37/// Build the network service, the network status sinks and an RPC sender.
38pub(crate) fn build_collator_network<Network: NetworkBackend<Block, Hash>>(
39	config: &Configuration,
40	mut network_config: FullNetworkConfiguration<Block, Hash, Network>,
41	spawn_handle: SpawnTaskHandle,
42	genesis_hash: Hash,
43	best_header: Header,
44	notification_metrics: NotificationMetrics,
45) -> Result<(Arc<dyn NetworkService>, Arc<dyn sp_consensus::SyncOracle + Send + Sync>), Error> {
46	let protocol_id = config.protocol_id();
47	let (block_announce_config, notification_service) = get_block_announce_proto_config::<Network>(
48		protocol_id.clone(),
49		&None,
50		Roles::from(&config.role),
51		best_header.number,
52		best_header.hash(),
53		genesis_hash,
54		notification_metrics.clone(),
55		network_config.peer_store_handle(),
56	);
57
58	// Since this node has no syncing, we do not want light-clients to connect to it.
59	// Here we set any potential light-client slots to 0.
60	adjust_network_config_light_in_peers(&mut network_config.network_config);
61
62	let peer_store = network_config.take_peer_store();
63	spawn_handle.spawn("peer-store", Some("networking"), peer_store.run());
64
65	let network_params = sc_network::config::Params::<Block, Hash, Network> {
66		role: config.role,
67		executor: {
68			let spawn_handle = Clone::clone(&spawn_handle);
69			Box::new(move |fut| {
70				spawn_handle.spawn("libp2p-node", Some("networking"), fut);
71			})
72		},
73		fork_id: None,
74		network_config,
75		genesis_hash,
76		protocol_id,
77		metrics_registry: config.prometheus_config.as_ref().map(|config| config.registry.clone()),
78		block_announce_config,
79		bitswap_config: None,
80		notification_metrics,
81	};
82
83	let network_worker = Network::new(network_params)?;
84	let network_service = network_worker.network_service();
85
86	// The network worker is responsible for gathering all network messages and processing
87	// them. This is quite a heavy task, and at the time of the writing of this comment it
88	// frequently happens that this future takes several seconds or in some situations
89	// even more than a minute until it has processed its entire queue. This is clearly an
90	// issue, and ideally we would like to fix the network future to take as little time as
91	// possible, but we also take the extra harm-prevention measure to execute the networking
92	// future using `spawn_blocking`.
93	spawn_handle.spawn_blocking("network-worker", Some("networking"), async move {
94		// The notification service must be kept alive to allow litep2p to handle
95		// requests under the hood. It has been noted that without the notification
96		// service of the `/block-announces/1` protocol, collators are not advertised
97		// and their produced blocks do not propagate:
98		// https://github.com/paritytech/polkadot-sdk/issues/8474
99		//
100		// This is because the full nodes on the relay chain will attempt to establish
101		// a connection to the minimal relay chain. By dropping the notification service,
102		// litep2p would terminate the background task which handles the `/block-announces/1`
103		// notification protocol. The downstream effect of this is that the full node
104		// would ban and disconnect the the minimal relay chain node.
105		let _notification_service = notification_service;
106		network_worker.run().await;
107	});
108
109	Ok((network_service, Arc::new(SyncOracle {})))
110}
111
112fn adjust_network_config_light_in_peers(config: &mut NetworkConfiguration) {
113	let light_client_in_peers = (config.default_peers_set.in_peers +
114		config.default_peers_set.out_peers)
115		.saturating_sub(config.default_peers_set_num_full);
116	if light_client_in_peers > 0 {
117		tracing::debug!(target: crate::LOG_TARGET, "Detected {light_client_in_peers} peer slots for light clients. Since this minimal node does support\
118											 neither syncing nor light-client request/response, we are setting them to 0.");
119	}
120	config.default_peers_set.in_peers =
121		config.default_peers_set.in_peers.saturating_sub(light_client_in_peers);
122}
123
124struct SyncOracle;
125
126impl sp_consensus::SyncOracle for SyncOracle {
127	fn is_major_syncing(&self) -> bool {
128		false
129	}
130
131	fn is_offline(&self) -> bool {
132		true
133	}
134}
135
136fn get_block_announce_proto_config<Network: NetworkBackend<Block, Hash>>(
137	protocol_id: ProtocolId,
138	fork_id: &Option<String>,
139	roles: Roles,
140	best_number: NumberFor<Block>,
141	best_hash: Hash,
142	genesis_hash: Hash,
143	metrics: NotificationMetrics,
144	peer_store_handle: Arc<dyn PeerStoreProvider>,
145) -> (Network::NotificationProtocolConfig, Box<dyn NotificationService>) {
146	let block_announces_protocol = {
147		let genesis_hash = genesis_hash.as_ref();
148		if let Some(ref fork_id) = fork_id {
149			format!("/{}/{}/block-announces/1", array_bytes::bytes2hex("", genesis_hash), fork_id)
150		} else {
151			format!("/{}/block-announces/1", array_bytes::bytes2hex("", genesis_hash))
152		}
153	};
154
155	Network::notification_config(
156		block_announces_protocol.into(),
157		iter::once(format!("/{}/block-announces/1", protocol_id.as_ref()).into()).collect(),
158		1024 * 1024,
159		Some(NotificationHandshake::new(BlockAnnouncesHandshake::<Block>::build(
160			roles,
161			best_number,
162			best_hash,
163			genesis_hash,
164		))),
165		// NOTE: `set_config` will be ignored by `protocol.rs` as the block announcement
166		// protocol is still hardcoded into the peerset.
167		SetConfig {
168			in_peers: 0,
169			out_peers: 0,
170			reserved_nodes: Vec::new(),
171			non_reserved_mode: NonReservedPeerMode::Deny,
172		},
173		metrics,
174		peer_store_handle,
175	)
176}