From 4def0da0dcae8350f4b48f33f3d568700f74deb6 Mon Sep 17 00:00:00 2001
From: Dmitry Markin <dmitry@markin.tech>
Date: Fri, 12 Aug 2022 15:07:13 +0300
Subject: [PATCH] Change request-response protocol names to include genesis
 hash & fork id (#5870)

---
 polkadot/Cargo.lock                           |  1 +
 .../src/tests/mod.rs                          | 11 ++-
 .../availability-recovery/src/tests.rs        | 13 ++-
 polkadot/node/network/bridge/src/network.rs   |  6 +-
 polkadot/node/network/bridge/src/rx/tests.rs  |  4 +-
 polkadot/node/network/bridge/src/tx/mod.rs    | 42 +++++++--
 polkadot/node/network/bridge/src/tx/tests.rs  | 11 ++-
 .../network/bridge/src/validator_discovery.rs |  6 +-
 .../src/collator_side/tests.rs                | 12 ++-
 .../dispute-distribution/src/tests/mod.rs     |  6 +-
 polkadot/node/network/protocol/Cargo.toml     |  1 +
 .../src/request_response/incoming/mod.rs      |  8 +-
 .../protocol/src/request_response/mod.rs      | 94 ++++++++++++++-----
 .../statement-distribution/src/tests.rs       | 22 +++--
 polkadot/node/service/src/lib.rs              | 30 +++---
 polkadot/node/service/src/overseer.rs         |  9 +-
 .../src/node/disputes/dispute-distribution.md |  4 +-
 17 files changed, 207 insertions(+), 73 deletions(-)

diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock
index d2d0d1e5389..fcaa07ff59c 100644
--- a/polkadot/Cargo.lock
+++ b/polkadot/Cargo.lock
@@ -6671,6 +6671,7 @@ dependencies = [
  "derive_more",
  "fatality",
  "futures",
+ "hex",
  "parity-scale-codec",
  "polkadot-node-jaeger",
  "polkadot-node-primitives",
diff --git a/polkadot/node/network/availability-distribution/src/tests/mod.rs b/polkadot/node/network/availability-distribution/src/tests/mod.rs
index fd5d0dafaf1..ebbc436a00d 100644
--- a/polkadot/node/network/availability-distribution/src/tests/mod.rs
+++ b/polkadot/node/network/availability-distribution/src/tests/mod.rs
@@ -18,8 +18,8 @@ use std::collections::HashSet;
 
 use futures::{executor, future, Future};
 
-use polkadot_node_network_protocol::request_response::IncomingRequest;
-use polkadot_primitives::v2::CoreState;
+use polkadot_node_network_protocol::request_response::{IncomingRequest, ReqProtocolNames};
+use polkadot_primitives::v2::{CoreState, Hash};
 use sp_keystore::SyncCryptoStorePtr;
 
 use polkadot_node_subsystem_test_helpers as test_helpers;
@@ -41,9 +41,12 @@ fn test_harness<T: Future<Output = ()>>(
 
 	let pool = sp_core::testing::TaskExecutor::new();
 	let (context, virtual_overseer) = test_helpers::make_subsystem_context(pool.clone());
+	let genesis_hash = Hash::repeat_byte(0xff);
+	let req_protocol_names = ReqProtocolNames::new(&genesis_hash, None);
 
-	let (pov_req_receiver, pov_req_cfg) = IncomingRequest::get_config_receiver();
-	let (chunk_req_receiver, chunk_req_cfg) = IncomingRequest::get_config_receiver();
+	let (pov_req_receiver, pov_req_cfg) = IncomingRequest::get_config_receiver(&req_protocol_names);
+	let (chunk_req_receiver, chunk_req_cfg) =
+		IncomingRequest::get_config_receiver(&req_protocol_names);
 	let subsystem = AvailabilityDistributionSubsystem::new(
 		keystore,
 		IncomingRequestReceivers { pov_req_receiver, chunk_req_receiver },
diff --git a/polkadot/node/network/availability-recovery/src/tests.rs b/polkadot/node/network/availability-recovery/src/tests.rs
index 3c16157f488..2cfed743bc5 100644
--- a/polkadot/node/network/availability-recovery/src/tests.rs
+++ b/polkadot/node/network/availability-recovery/src/tests.rs
@@ -21,7 +21,7 @@ use futures::{executor, future};
 use futures_timer::Delay;
 
 use parity_scale_codec::Encode;
-use polkadot_node_network_protocol::request_response::IncomingRequest;
+use polkadot_node_network_protocol::request_response::{IncomingRequest, ReqProtocolNames};
 
 use super::*;
 
@@ -36,11 +36,14 @@ use polkadot_node_subsystem::{
 };
 use polkadot_node_subsystem_test_helpers::{make_subsystem_context, TestSubsystemContextHandle};
 use polkadot_node_subsystem_util::TimeoutExt;
-use polkadot_primitives::v2::{AuthorityDiscoveryId, HeadData, PersistedValidationData};
+use polkadot_primitives::v2::{AuthorityDiscoveryId, Hash, HeadData, PersistedValidationData};
 use polkadot_primitives_test_helpers::{dummy_candidate_receipt, dummy_hash};
 
 type VirtualOverseer = TestSubsystemContextHandle<AvailabilityRecoveryMessage>;
 
+// Deterministic genesis hash for protocol names
+const GENESIS_HASH: Hash = Hash::repeat_byte(0xff);
+
 fn test_harness_fast_path<T: Future<Output = (VirtualOverseer, RequestResponseConfig)>>(
 	test: impl FnOnce(VirtualOverseer, RequestResponseConfig) -> T,
 ) {
@@ -53,7 +56,8 @@ fn test_harness_fast_path<T: Future<Output = (VirtualOverseer, RequestResponseCo
 
 	let (context, virtual_overseer) = make_subsystem_context(pool.clone());
 
-	let (collation_req_receiver, req_cfg) = IncomingRequest::get_config_receiver();
+	let (collation_req_receiver, req_cfg) =
+		IncomingRequest::get_config_receiver(&ReqProtocolNames::new(&GENESIS_HASH, None));
 	let subsystem =
 		AvailabilityRecoverySubsystem::with_fast_path(collation_req_receiver, Metrics::new_dummy());
 	let subsystem = async {
@@ -87,7 +91,8 @@ fn test_harness_chunks_only<T: Future<Output = (VirtualOverseer, RequestResponse
 
 	let (context, virtual_overseer) = make_subsystem_context(pool.clone());
 
-	let (collation_req_receiver, req_cfg) = IncomingRequest::get_config_receiver();
+	let (collation_req_receiver, req_cfg) =
+		IncomingRequest::get_config_receiver(&ReqProtocolNames::new(&GENESIS_HASH, None));
 	let subsystem = AvailabilityRecoverySubsystem::with_chunks_only(
 		collation_req_receiver,
 		Metrics::new_dummy(),
diff --git a/polkadot/node/network/bridge/src/network.rs b/polkadot/node/network/bridge/src/network.rs
index cea6283202b..00a950f35c5 100644
--- a/polkadot/node/network/bridge/src/network.rs
+++ b/polkadot/node/network/bridge/src/network.rs
@@ -31,7 +31,7 @@ use sc_network_common::service::{
 
 use polkadot_node_network_protocol::{
 	peer_set::PeerSet,
-	request_response::{OutgoingRequest, Recipient, Requests},
+	request_response::{OutgoingRequest, Recipient, ReqProtocolNames, Requests},
 	PeerId, ProtocolVersion, UnifiedReputationChange as Rep,
 };
 use polkadot_primitives::v2::{AuthorityDiscoveryId, Block, Hash};
@@ -100,6 +100,7 @@ pub trait Network: Clone + Send + 'static {
 		&self,
 		authority_discovery: &mut AD,
 		req: Requests,
+		req_protocol_names: &ReqProtocolNames,
 		if_disconnected: IfDisconnected,
 	);
 
@@ -152,6 +153,7 @@ impl Network for Arc<NetworkService<Block, Hash>> {
 		&self,
 		authority_discovery: &mut AD,
 		req: Requests,
+		req_protocol_names: &ReqProtocolNames,
 		if_disconnected: IfDisconnected,
 	) {
 		let (protocol, OutgoingRequest { peer, payload, pending_response }) = req.encode_request();
@@ -197,7 +199,7 @@ impl Network for Arc<NetworkService<Block, Hash>> {
 		NetworkService::start_request(
 			&*self,
 			peer_id,
-			protocol.into_protocol_name(),
+			req_protocol_names.get_name(protocol),
 			payload,
 			pending_response,
 			if_disconnected,
diff --git a/polkadot/node/network/bridge/src/rx/tests.rs b/polkadot/node/network/bridge/src/rx/tests.rs
index 4e4ce923d8c..ad1d8392d30 100644
--- a/polkadot/node/network/bridge/src/rx/tests.rs
+++ b/polkadot/node/network/bridge/src/rx/tests.rs
@@ -31,7 +31,8 @@ use std::{
 use sc_network::{Event as NetworkEvent, IfDisconnected};
 
 use polkadot_node_network_protocol::{
-	request_response::outgoing::Requests, view, ObservedRole, Versioned,
+	request_response::{outgoing::Requests, ReqProtocolNames},
+	view, ObservedRole, Versioned,
 };
 use polkadot_node_subsystem::{
 	jaeger,
@@ -117,6 +118,7 @@ impl Network for TestNetwork {
 		&self,
 		_: &mut AD,
 		_: Requests,
+		_: &ReqProtocolNames,
 		_: IfDisconnected,
 	) {
 	}
diff --git a/polkadot/node/network/bridge/src/tx/mod.rs b/polkadot/node/network/bridge/src/tx/mod.rs
index d58ccf8fbb9..ac34cf315ce 100644
--- a/polkadot/node/network/bridge/src/tx/mod.rs
+++ b/polkadot/node/network/bridge/src/tx/mod.rs
@@ -17,7 +17,9 @@
 //! The Network Bridge Subsystem - handles _outgoing_ messages, from subsystem to the network.
 use super::*;
 
-use polkadot_node_network_protocol::{peer_set::PeerSet, v1 as protocol_v1, PeerId, Versioned};
+use polkadot_node_network_protocol::{
+	peer_set::PeerSet, request_response::ReqProtocolNames, v1 as protocol_v1, PeerId, Versioned,
+};
 
 use polkadot_node_subsystem::{
 	errors::SubsystemError, messages::NetworkBridgeTxMessage, overseer, FromOrchestra,
@@ -50,6 +52,7 @@ pub struct NetworkBridgeTx<N, AD> {
 	network_service: N,
 	authority_discovery_service: AD,
 	metrics: Metrics,
+	req_protocol_names: ReqProtocolNames,
 }
 
 impl<N, AD> NetworkBridgeTx<N, AD> {
@@ -57,8 +60,13 @@ impl<N, AD> NetworkBridgeTx<N, AD> {
 	///
 	/// This assumes that the network service has had the notifications protocol for the network
 	/// bridge already registered. See [`peers_sets_info`](peers_sets_info).
-	pub fn new(network_service: N, authority_discovery_service: AD, metrics: Metrics) -> Self {
-		Self { network_service, authority_discovery_service, metrics }
+	pub fn new(
+		network_service: N,
+		authority_discovery_service: AD,
+		metrics: Metrics,
+		req_protocol_names: ReqProtocolNames,
+	) -> Self {
+		Self { network_service, authority_discovery_service, metrics, req_protocol_names }
 	}
 }
 
@@ -82,6 +90,7 @@ async fn handle_subsystem_messages<Context, N, AD>(
 	mut network_service: N,
 	mut authority_discovery_service: AD,
 	metrics: Metrics,
+	req_protocol_names: ReqProtocolNames,
 ) -> Result<(), Error>
 where
 	N: Network,
@@ -102,6 +111,7 @@ where
 						authority_discovery_service.clone(),
 						msg,
 						&metrics,
+						&req_protocol_names,
 					)
 					.await;
 			},
@@ -117,6 +127,7 @@ async fn handle_incoming_subsystem_communication<Context, N, AD>(
 	mut authority_discovery_service: AD,
 	msg: NetworkBridgeTxMessage,
 	metrics: &Metrics,
+	req_protocol_names: &ReqProtocolNames,
 ) -> (N, AD)
 where
 	N: Network,
@@ -218,7 +229,12 @@ where
 
 			for req in reqs {
 				network_service
-					.start_request(&mut authority_discovery_service, req, if_disconnected)
+					.start_request(
+						&mut authority_discovery_service,
+						req,
+						req_protocol_names,
+						if_disconnected,
+					)
 					.await;
 			}
 		},
@@ -275,9 +291,21 @@ where
 	N: Network,
 	AD: validator_discovery::AuthorityDiscovery + Clone + Sync,
 {
-	let NetworkBridgeTx { network_service, authority_discovery_service, metrics } = bridge;
-
-	handle_subsystem_messages(ctx, network_service, authority_discovery_service, metrics).await?;
+	let NetworkBridgeTx {
+		network_service,
+		authority_discovery_service,
+		metrics,
+		req_protocol_names,
+	} = bridge;
+
+	handle_subsystem_messages(
+		ctx,
+		network_service,
+		authority_discovery_service,
+		metrics,
+		req_protocol_names,
+	)
+	.await?;
 
 	Ok(())
 }
diff --git a/polkadot/node/network/bridge/src/tx/tests.rs b/polkadot/node/network/bridge/src/tx/tests.rs
index 63d9730e659..d5b6d3ca67a 100644
--- a/polkadot/node/network/bridge/src/tx/tests.rs
+++ b/polkadot/node/network/bridge/src/tx/tests.rs
@@ -25,12 +25,13 @@ use std::{borrow::Cow, collections::HashSet};
 use sc_network::{Event as NetworkEvent, IfDisconnected};
 
 use polkadot_node_network_protocol::{
-	request_response::outgoing::Requests, ObservedRole, Versioned,
+	request_response::{outgoing::Requests, ReqProtocolNames},
+	ObservedRole, Versioned,
 };
 use polkadot_node_subsystem::{FromOrchestra, OverseerSignal};
 use polkadot_node_subsystem_test_helpers::TestSubsystemContextHandle;
 use polkadot_node_subsystem_util::metered;
-use polkadot_primitives::v2::AuthorityDiscoveryId;
+use polkadot_primitives::v2::{AuthorityDiscoveryId, Hash};
 use polkadot_primitives_test_helpers::dummy_collator_signature;
 use sc_network::Multiaddr;
 use sp_keyring::Sr25519Keyring;
@@ -104,6 +105,7 @@ impl Network for TestNetwork {
 		&self,
 		_: &mut AD,
 		_: Requests,
+		_: &ReqProtocolNames,
 		_: IfDisconnected,
 	) {
 	}
@@ -182,7 +184,10 @@ fn test_harness<T: Future<Output = VirtualOverseer>>(test: impl FnOnce(TestHarne
 	let (context, virtual_overseer) =
 		polkadot_node_subsystem_test_helpers::make_subsystem_context(pool);
 
-	let bridge_out = NetworkBridgeTx::new(network, discovery, Metrics(None));
+	let genesis_hash = Hash::repeat_byte(0xff);
+	let protocol_names = ReqProtocolNames::new(genesis_hash, None);
+
+	let bridge_out = NetworkBridgeTx::new(network, discovery, Metrics(None), protocol_names);
 
 	let network_bridge_out_fut = run_network_out(bridge_out, context)
 		.map_err(|e| panic!("bridge-out subsystem execution failed {:?}", e))
diff --git a/polkadot/node/network/bridge/src/validator_discovery.rs b/polkadot/node/network/bridge/src/validator_discovery.rs
index eb9bb954e7a..9c90200aa06 100644
--- a/polkadot/node/network/bridge/src/validator_discovery.rs
+++ b/polkadot/node/network/bridge/src/validator_discovery.rs
@@ -162,7 +162,10 @@ mod tests {
 
 	use async_trait::async_trait;
 	use futures::stream::BoxStream;
-	use polkadot_node_network_protocol::{request_response::outgoing::Requests, PeerId};
+	use polkadot_node_network_protocol::{
+		request_response::{outgoing::Requests, ReqProtocolNames},
+		PeerId,
+	};
 	use sc_network::{Event as NetworkEvent, IfDisconnected};
 	use sp_keyring::Sr25519Keyring;
 	use std::{
@@ -236,6 +239,7 @@ mod tests {
 			&self,
 			_: &mut AD,
 			_: Requests,
+			_: &ReqProtocolNames,
 			_: IfDisconnected,
 		) {
 		}
diff --git a/polkadot/node/network/collator-protocol/src/collator_side/tests.rs b/polkadot/node/network/collator-protocol/src/collator_side/tests.rs
index 4d95b7c807e..f81e738e16a 100644
--- a/polkadot/node/network/collator-protocol/src/collator_side/tests.rs
+++ b/polkadot/node/network/collator-protocol/src/collator_side/tests.rs
@@ -29,7 +29,11 @@ use sp_core::crypto::Pair;
 use sp_keyring::Sr25519Keyring;
 use sp_runtime::traits::AppVerify;
 
-use polkadot_node_network_protocol::{our_view, request_response::IncomingRequest, view};
+use polkadot_node_network_protocol::{
+	our_view,
+	request_response::{IncomingRequest, ReqProtocolNames},
+	view,
+};
 use polkadot_node_primitives::BlockData;
 use polkadot_node_subsystem::{
 	jaeger,
@@ -201,7 +205,11 @@ fn test_harness<T: Future<Output = TestHarness>>(
 
 	let (context, virtual_overseer) = test_helpers::make_subsystem_context(pool.clone());
 
-	let (collation_req_receiver, req_cfg) = IncomingRequest::get_config_receiver();
+	let genesis_hash = Hash::repeat_byte(0xff);
+	let req_protocol_names = ReqProtocolNames::new(&genesis_hash, None);
+
+	let (collation_req_receiver, req_cfg) =
+		IncomingRequest::get_config_receiver(&req_protocol_names);
 	let subsystem = async {
 		run(context, local_peer_id, collator_pair, collation_req_receiver, Default::default())
 			.await
diff --git a/polkadot/node/network/dispute-distribution/src/tests/mod.rs b/polkadot/node/network/dispute-distribution/src/tests/mod.rs
index 9c843f3e786..8f78b664de8 100644
--- a/polkadot/node/network/dispute-distribution/src/tests/mod.rs
+++ b/polkadot/node/network/dispute-distribution/src/tests/mod.rs
@@ -31,7 +31,7 @@ use parity_scale_codec::{Decode, Encode};
 use sc_network::config::RequestResponseConfig;
 
 use polkadot_node_network_protocol::{
-	request_response::{v1::DisputeRequest, IncomingRequest},
+	request_response::{v1::DisputeRequest, IncomingRequest, ReqProtocolNames},
 	PeerId,
 };
 use sp_keyring::Sr25519Keyring;
@@ -723,7 +723,9 @@ where
 	sp_tracing::try_init_simple();
 	let keystore = make_ferdie_keystore();
 
-	let (req_receiver, req_cfg) = IncomingRequest::get_config_receiver();
+	let genesis_hash = Hash::repeat_byte(0xff);
+	let req_protocol_names = ReqProtocolNames::new(&genesis_hash, None);
+	let (req_receiver, req_cfg) = IncomingRequest::get_config_receiver(&req_protocol_names);
 	let subsystem = DisputeDistributionSubsystem::new(
 		keystore,
 		req_receiver,
diff --git a/polkadot/node/network/protocol/Cargo.toml b/polkadot/node/network/protocol/Cargo.toml
index 7b26a03e3d4..8b863089cc9 100644
--- a/polkadot/node/network/protocol/Cargo.toml
+++ b/polkadot/node/network/protocol/Cargo.toml
@@ -7,6 +7,7 @@ description = "Primitives types for the Node-side"
 
 [dependencies]
 async-trait = "0.1.53"
+hex = "0.4.3"
 polkadot-primitives = { path = "../../../primitives" }
 polkadot-node-primitives = { path = "../../primitives" }
 polkadot-node-jaeger = { path = "../../jaeger" }
diff --git a/polkadot/node/network/protocol/src/request_response/incoming/mod.rs b/polkadot/node/network/protocol/src/request_response/incoming/mod.rs
index 309ca32b0de..808d7064599 100644
--- a/polkadot/node/network/protocol/src/request_response/incoming/mod.rs
+++ b/polkadot/node/network/protocol/src/request_response/incoming/mod.rs
@@ -25,7 +25,7 @@ use parity_scale_codec::{Decode, Encode};
 
 use sc_network::{config as netconfig, config::RequestResponseConfig, PeerId};
 
-use super::IsRequest;
+use super::{IsRequest, ReqProtocolNames};
 use crate::UnifiedReputationChange;
 
 mod error;
@@ -55,8 +55,10 @@ where
 	///
 	/// This Register that config with substrate networking and receive incoming requests via the
 	/// returned `IncomingRequestReceiver`.
-	pub fn get_config_receiver() -> (IncomingRequestReceiver<Req>, RequestResponseConfig) {
-		let (raw, cfg) = Req::PROTOCOL.get_config();
+	pub fn get_config_receiver(
+		req_protocol_names: &ReqProtocolNames,
+	) -> (IncomingRequestReceiver<Req>, RequestResponseConfig) {
+		let (raw, cfg) = Req::PROTOCOL.get_config(req_protocol_names);
 		(IncomingRequestReceiver { raw, phantom: PhantomData {} }, cfg)
 	}
 
diff --git a/polkadot/node/network/protocol/src/request_response/mod.rs b/polkadot/node/network/protocol/src/request_response/mod.rs
index b434c152b89..fb955286990 100644
--- a/polkadot/node/network/protocol/src/request_response/mod.rs
+++ b/polkadot/node/network/protocol/src/request_response/mod.rs
@@ -32,11 +32,11 @@
 //!
 //!  Versioned (v1 module): The actual requests and responses as sent over the network.
 
-use std::{borrow::Cow, time::Duration, u64};
+use std::{borrow::Cow, collections::HashMap, time::Duration, u64};
 
 use futures::channel::mpsc;
 use polkadot_primitives::v2::{MAX_CODE_SIZE, MAX_POV_SIZE};
-use strum::EnumIter;
+use strum::{EnumIter, IntoEnumIterator};
 
 pub use sc_network::{config as network, config::RequestResponseConfig};
 
@@ -126,13 +126,17 @@ impl Protocol {
 	///
 	/// Returns a receiver for messages received on this protocol and the requested
 	/// `ProtocolConfig`.
-	pub fn get_config(self) -> (mpsc::Receiver<network::IncomingRequest>, RequestResponseConfig) {
-		let p_name = self.into_protocol_name();
+	pub fn get_config(
+		self,
+		req_protocol_names: &ReqProtocolNames,
+	) -> (mpsc::Receiver<network::IncomingRequest>, RequestResponseConfig) {
+		let name = req_protocol_names.get_name(self);
+		let fallback_names = self.get_fallback_names();
 		let (tx, rx) = mpsc::channel(self.get_channel_size());
 		let cfg = match self {
 			Protocol::ChunkFetchingV1 => RequestResponseConfig {
-				name: p_name,
-				fallback_names: Vec::new(),
+				name,
+				fallback_names,
 				max_request_size: 1_000,
 				max_response_size: POV_RESPONSE_SIZE as u64 * 3,
 				// We are connected to all validators:
@@ -140,8 +144,8 @@ impl Protocol {
 				inbound_queue: Some(tx),
 			},
 			Protocol::CollationFetchingV1 => RequestResponseConfig {
-				name: p_name,
-				fallback_names: Vec::new(),
+				name,
+				fallback_names,
 				max_request_size: 1_000,
 				max_response_size: POV_RESPONSE_SIZE,
 				// Taken from initial implementation in collator protocol:
@@ -149,16 +153,16 @@ impl Protocol {
 				inbound_queue: Some(tx),
 			},
 			Protocol::PoVFetchingV1 => RequestResponseConfig {
-				name: p_name,
-				fallback_names: Vec::new(),
+				name,
+				fallback_names,
 				max_request_size: 1_000,
 				max_response_size: POV_RESPONSE_SIZE,
 				request_timeout: POV_REQUEST_TIMEOUT_CONNECTED,
 				inbound_queue: Some(tx),
 			},
 			Protocol::AvailableDataFetchingV1 => RequestResponseConfig {
-				name: p_name,
-				fallback_names: Vec::new(),
+				name,
+				fallback_names,
 				max_request_size: 1_000,
 				// Available data size is dominated by the PoV size.
 				max_response_size: POV_RESPONSE_SIZE,
@@ -166,8 +170,8 @@ impl Protocol {
 				inbound_queue: Some(tx),
 			},
 			Protocol::StatementFetchingV1 => RequestResponseConfig {
-				name: p_name,
-				fallback_names: Vec::new(),
+				name,
+				fallback_names,
 				max_request_size: 1_000,
 				// Available data size is dominated code size.
 				max_response_size: STATEMENT_RESPONSE_SIZE,
@@ -184,8 +188,8 @@ impl Protocol {
 				inbound_queue: Some(tx),
 			},
 			Protocol::DisputeSendingV1 => RequestResponseConfig {
-				name: p_name,
-				fallback_names: Vec::new(),
+				name,
+				fallback_names,
 				max_request_size: 1_000,
 				/// Responses are just confirmation, in essence not even a bit. So 100 seems
 				/// plenty.
@@ -243,13 +247,13 @@ impl Protocol {
 		}
 	}
 
-	/// Get the protocol name of this protocol, as understood by substrate networking.
-	pub fn into_protocol_name(self) -> Cow<'static, str> {
-		self.get_protocol_name_static().into()
+	/// Fallback protocol names of this protocol, as understood by substrate networking.
+	fn get_fallback_names(self) -> Vec<Cow<'static, str>> {
+		std::iter::once(self.get_legacy_name().into()).collect()
 	}
 
-	/// Get the protocol name associated with each peer set as static str.
-	pub const fn get_protocol_name_static(self) -> &'static str {
+	/// Legacy protocol name associated with each peer set.
+	const fn get_legacy_name(self) -> &'static str {
 		match self {
 			Protocol::ChunkFetchingV1 => "/polkadot/req_chunk/1",
 			Protocol::CollationFetchingV1 => "/polkadot/req_collation/1",
@@ -269,3 +273,51 @@ pub trait IsRequest {
 	/// What protocol this `Request` implements.
 	const PROTOCOL: Protocol;
 }
+
+/// Type for getting on the wire [`Protocol`] names using genesis hash & fork id.
+pub struct ReqProtocolNames {
+	names: HashMap<Protocol, Cow<'static, str>>,
+}
+
+impl ReqProtocolNames {
+	/// Construct [`ReqProtocolNames`] from `genesis_hash` and `fork_id`.
+	pub fn new<Hash: AsRef<[u8]>>(genesis_hash: Hash, fork_id: Option<&str>) -> Self {
+		let mut names = HashMap::new();
+		for protocol in Protocol::iter() {
+			names.insert(protocol, Self::generate_name(protocol, &genesis_hash, fork_id));
+		}
+		Self { names }
+	}
+
+	/// Get on the wire [`Protocol`] name.
+	pub fn get_name(&self, protocol: Protocol) -> Cow<'static, str> {
+		self.names
+			.get(&protocol)
+			.expect("All `Protocol` enum variants are added above via `strum`; qed")
+			.clone()
+	}
+
+	/// Protocol name of this protocol based on `genesis_hash` and `fork_id`.
+	fn generate_name<Hash: AsRef<[u8]>>(
+		protocol: Protocol,
+		genesis_hash: &Hash,
+		fork_id: Option<&str>,
+	) -> Cow<'static, str> {
+		let prefix = if let Some(fork_id) = fork_id {
+			format!("/{}/{}", hex::encode(genesis_hash), fork_id)
+		} else {
+			format!("/{}", hex::encode(genesis_hash))
+		};
+
+		let short_name = match protocol {
+			Protocol::ChunkFetchingV1 => "/req_chunk/1",
+			Protocol::CollationFetchingV1 => "/req_collation/1",
+			Protocol::PoVFetchingV1 => "/req_pov/1",
+			Protocol::AvailableDataFetchingV1 => "/req_available_data/1",
+			Protocol::StatementFetchingV1 => "/req_statement/1",
+			Protocol::DisputeSendingV1 => "/send_dispute/1",
+		};
+
+		format!("{}{}", prefix, short_name).into()
+	}
+}
diff --git a/polkadot/node/network/statement-distribution/src/tests.rs b/polkadot/node/network/statement-distribution/src/tests.rs
index 49a6e211bbd..efc9ca896bb 100644
--- a/polkadot/node/network/statement-distribution/src/tests.rs
+++ b/polkadot/node/network/statement-distribution/src/tests.rs
@@ -22,7 +22,7 @@ use parity_scale_codec::{Decode, Encode};
 use polkadot_node_network_protocol::{
 	request_response::{
 		v1::{StatementFetchingRequest, StatementFetchingResponse},
-		IncomingRequest, Recipient, Requests,
+		IncomingRequest, Recipient, ReqProtocolNames, Requests,
 	},
 	view, ObservedRole,
 };
@@ -44,6 +44,9 @@ use sp_keyring::Sr25519Keyring;
 use sp_keystore::{CryptoStore, SyncCryptoStore, SyncCryptoStorePtr};
 use std::{iter::FromIterator as _, sync::Arc, time::Duration};
 
+// Some deterministic genesis hash for protocol names
+const GENESIS_HASH: Hash = Hash::repeat_byte(0xff);
+
 #[test]
 fn active_head_accepts_only_2_seconded_per_validator() {
 	let validators = vec![
@@ -724,7 +727,8 @@ fn receiving_from_one_sends_to_another_and_to_candidate_backing() {
 	let pool = sp_core::testing::TaskExecutor::new();
 	let (ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool);
 
-	let (statement_req_receiver, _) = IncomingRequest::get_config_receiver();
+	let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
+	let (statement_req_receiver, _) = IncomingRequest::get_config_receiver(&req_protocol_names);
 
 	let bg = async move {
 		let s = StatementDistributionSubsystem::new(
@@ -917,7 +921,9 @@ fn receiving_large_statement_from_one_sends_to_another_and_to_candidate_backing(
 	let pool = sp_core::testing::TaskExecutor::new();
 	let (ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool);
 
-	let (statement_req_receiver, mut req_cfg) = IncomingRequest::get_config_receiver();
+	let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
+	let (statement_req_receiver, mut req_cfg) =
+		IncomingRequest::get_config_receiver(&req_protocol_names);
 
 	let bg = async move {
 		let s = StatementDistributionSubsystem::new(
@@ -1429,7 +1435,9 @@ fn share_prioritizes_backing_group() {
 	let pool = sp_core::testing::TaskExecutor::new();
 	let (ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool);
 
-	let (statement_req_receiver, mut req_cfg) = IncomingRequest::get_config_receiver();
+	let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
+	let (statement_req_receiver, mut req_cfg) =
+		IncomingRequest::get_config_receiver(&req_protocol_names);
 
 	let bg = async move {
 		let s = StatementDistributionSubsystem::new(
@@ -1724,7 +1732,8 @@ fn peer_cant_flood_with_large_statements() {
 	let pool = sp_core::testing::TaskExecutor::new();
 	let (ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool);
 
-	let (statement_req_receiver, _) = IncomingRequest::get_config_receiver();
+	let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
+	let (statement_req_receiver, _) = IncomingRequest::get_config_receiver(&req_protocol_names);
 	let bg = async move {
 		let s = StatementDistributionSubsystem::new(
 			make_ferdie_keystore(),
@@ -1928,7 +1937,8 @@ fn handle_multiple_seconded_statements() {
 	let pool = sp_core::testing::TaskExecutor::new();
 	let (ctx, mut handle) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool);
 
-	let (statement_req_receiver, _) = IncomingRequest::get_config_receiver();
+	let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
+	let (statement_req_receiver, _) = IncomingRequest::get_config_receiver(&req_protocol_names);
 
 	let virtual_overseer_fut = async move {
 		let s = StatementDistributionSubsystem::new(
diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs
index d12b2ccadb3..206809aeefe 100644
--- a/polkadot/node/service/src/lib.rs
+++ b/polkadot/node/service/src/lib.rs
@@ -46,6 +46,7 @@ use {
 		self as chain_selection_subsystem, Config as ChainSelectionConfig,
 	},
 	polkadot_node_core_dispute_coordinator::Config as DisputeCoordinatorConfig,
+	polkadot_node_network_protocol::request_response::ReqProtocolNames,
 	polkadot_overseer::BlockInfo,
 	sc_client_api::{BlockBackend, ExecutorProvider},
 	sp_core::traits::SpawnNamed,
@@ -831,22 +832,19 @@ where
 	let shared_voter_state = rpc_setup;
 	let auth_disc_publish_non_global_ips = config.network.allow_non_globals_in_dht;
 
+	let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed");
+
 	// Note: GrandPa is pushed before the Polkadot-specific protocols. This doesn't change
 	// anything in terms of behaviour, but makes the logs more consistent with the other
 	// Substrate nodes.
-	let grandpa_protocol_name = grandpa::protocol_standard_name(
-		&client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"),
-		&config.chain_spec,
-	);
+	let grandpa_protocol_name = grandpa::protocol_standard_name(&genesis_hash, &config.chain_spec);
 	config
 		.network
 		.extra_sets
 		.push(grandpa::grandpa_peers_set_config(grandpa_protocol_name.clone()));
 
-	let beefy_protocol_name = beefy_gadget::protocol_standard_name(
-		&client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"),
-		&config.chain_spec,
-	);
+	let beefy_protocol_name =
+		beefy_gadget::protocol_standard_name(&genesis_hash, &config.chain_spec);
 	if enable_beefy {
 		config
 			.network
@@ -860,17 +858,20 @@ where
 		config.network.extra_sets.extend(peer_sets_info(is_authority));
 	}
 
-	let (pov_req_receiver, cfg) = IncomingRequest::get_config_receiver();
+	let req_protocol_names = ReqProtocolNames::new(&genesis_hash, config.chain_spec.fork_id());
+
+	let (pov_req_receiver, cfg) = IncomingRequest::get_config_receiver(&req_protocol_names);
 	config.network.request_response_protocols.push(cfg);
-	let (chunk_req_receiver, cfg) = IncomingRequest::get_config_receiver();
+	let (chunk_req_receiver, cfg) = IncomingRequest::get_config_receiver(&req_protocol_names);
 	config.network.request_response_protocols.push(cfg);
-	let (collation_req_receiver, cfg) = IncomingRequest::get_config_receiver();
+	let (collation_req_receiver, cfg) = IncomingRequest::get_config_receiver(&req_protocol_names);
 	config.network.request_response_protocols.push(cfg);
-	let (available_data_req_receiver, cfg) = IncomingRequest::get_config_receiver();
+	let (available_data_req_receiver, cfg) =
+		IncomingRequest::get_config_receiver(&req_protocol_names);
 	config.network.request_response_protocols.push(cfg);
-	let (statement_req_receiver, cfg) = IncomingRequest::get_config_receiver();
+	let (statement_req_receiver, cfg) = IncomingRequest::get_config_receiver(&req_protocol_names);
 	config.network.request_response_protocols.push(cfg);
-	let (dispute_req_receiver, cfg) = IncomingRequest::get_config_receiver();
+	let (dispute_req_receiver, cfg) = IncomingRequest::get_config_receiver(&req_protocol_names);
 	config.network.request_response_protocols.push(cfg);
 
 	let grandpa_hard_forks = if config.chain_spec.is_kusama() {
@@ -1061,6 +1062,7 @@ where
 					dispute_coordinator_config,
 					pvf_checker_enabled,
 					overseer_message_channel_capacity_override,
+					req_protocol_names,
 				},
 			)
 			.map_err(|e| {
diff --git a/polkadot/node/service/src/overseer.rs b/polkadot/node/service/src/overseer.rs
index c150dd35e72..622b815944a 100644
--- a/polkadot/node/service/src/overseer.rs
+++ b/polkadot/node/service/src/overseer.rs
@@ -24,7 +24,9 @@ use polkadot_node_core_av_store::Config as AvailabilityConfig;
 use polkadot_node_core_candidate_validation::Config as CandidateValidationConfig;
 use polkadot_node_core_chain_selection::Config as ChainSelectionConfig;
 use polkadot_node_core_dispute_coordinator::Config as DisputeCoordinatorConfig;
-use polkadot_node_network_protocol::request_response::{v1 as request_v1, IncomingRequestReceiver};
+use polkadot_node_network_protocol::request_response::{
+	v1 as request_v1, IncomingRequestReceiver, ReqProtocolNames,
+};
 #[cfg(any(feature = "malus", test))]
 pub use polkadot_overseer::{
 	dummy::{dummy_overseer_builder, DummySubsystem},
@@ -118,6 +120,8 @@ where
 	pub pvf_checker_enabled: bool,
 	/// Overseer channel capacity override.
 	pub overseer_message_channel_capacity_override: Option<usize>,
+	/// Request-response protocol names source.
+	pub req_protocol_names: ReqProtocolNames,
 }
 
 /// Obtain a prepared `OverseerBuilder`, that is initialized
@@ -146,6 +150,7 @@ pub fn prepared_overseer_builder<'a, Spawner, RuntimeClient>(
 		dispute_coordinator_config,
 		pvf_checker_enabled,
 		overseer_message_channel_capacity_override,
+		req_protocol_names,
 	}: OverseerGenArgs<'a, Spawner, RuntimeClient>,
 ) -> Result<
 	InitializedOverseerBuilder<
@@ -194,11 +199,13 @@ where
 	let spawner = SpawnGlue(spawner);
 
 	let network_bridge_metrics: NetworkBridgeMetrics = Metrics::register(registry)?;
+
 	let builder = Overseer::builder()
 		.network_bridge_tx(NetworkBridgeTxSubsystem::new(
 			network_service.clone(),
 			authority_discovery_service.clone(),
 			network_bridge_metrics.clone(),
+			req_protocol_names,
 		))
 		.network_bridge_rx(NetworkBridgeRxSubsystem::new(
 			network_service.clone(),
diff --git a/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-distribution.md b/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-distribution.md
index eb571420fb7..579a1ce8f14 100644
--- a/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-distribution.md
+++ b/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-distribution.md
@@ -30,7 +30,7 @@ This design should result in a protocol that is:
 
 #### Disputes
 
-Protocol: `"/polkadot/send_dispute/1"`
+Protocol: `"/<genesis_hash>/<fork_id>/send_dispute/1"`
 
 Request:
 
@@ -86,7 +86,7 @@ enum DisputeResponse {
 
 #### Vote Recovery
 
-Protocol: `"/polkadot/req_votes/1"`
+Protocol: `"/<genesis_hash>/<fork_id>/req_votes/1"`
 
 ```rust
 struct IHaveVotesRequest {
-- 
GitLab