diff --git a/Cargo.lock b/Cargo.lock
index 1ad880641445..bb9c305c1084 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -7936,6 +7936,7 @@ dependencies = [
"pallet-transaction-payment-rpc-runtime-api",
"parity-db",
"parity-scale-codec",
+ "parking_lot 0.12.1",
"polkadot-approval-distribution",
"polkadot-availability-bitfield-distribution",
"polkadot-availability-distribution",
diff --git a/node/network/bridge/Cargo.toml b/node/network/bridge/Cargo.toml
index 4f3d6306aa8e..56b506df627d 100644
--- a/node/network/bridge/Cargo.toml
+++ b/node/network/bridge/Cargo.toml
@@ -6,7 +6,6 @@ edition.workspace = true
license.workspace = true
[dependencies]
-always-assert = "0.1"
async-trait = "0.1.57"
futures = "0.3.21"
gum = { package = "tracing-gum", path = "../../gum" }
diff --git a/node/network/bridge/src/network.rs b/node/network/bridge/src/network.rs
index 04026197f6eb..42f734b9ff67 100644
--- a/node/network/bridge/src/network.rs
+++ b/node/network/bridge/src/network.rs
@@ -14,21 +14,24 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see .
-use std::{collections::HashSet, sync::Arc};
+use std::{
+ collections::{HashMap, HashSet},
+ sync::Arc,
+};
use async_trait::async_trait;
-use futures::{prelude::*, stream::BoxStream};
+use parking_lot::Mutex;
use parity_scale_codec::Encode;
use sc_network::{
- config::parse_addr, multiaddr::Multiaddr, types::ProtocolName, Event as NetworkEvent,
- IfDisconnected, NetworkEventStream, NetworkNotification, NetworkPeers, NetworkRequest,
- NetworkService, OutboundFailure, ReputationChange, RequestFailure,
+ config::parse_addr, multiaddr::Multiaddr, service::traits::MessageSink, types::ProtocolName,
+ IfDisconnected, NetworkPeers, NetworkRequest, NetworkService, ObservedRole, OutboundFailure,
+ ReputationChange, RequestFailure,
};
use polkadot_node_network_protocol::{
- peer_set::{PeerSet, PeerSetProtocolNames, ProtocolVersion},
+ peer_set::{PeerSet, ProtocolVersion},
request_response::{OutgoingRequest, Recipient, ReqProtocolNames, Requests},
PeerId,
};
@@ -45,13 +48,12 @@ const LOG_TARGET: &'static str = "parachain::network-bridge-net";
/// messages that are compatible with the passed peer set, as that is currently not enforced by
/// this function. These are messages of type `WireMessage` parameterized on the matching type.
pub(crate) fn send_message(
- net: &mut impl Network,
mut peers: Vec,
peer_set: PeerSet,
version: ProtocolVersion,
- protocol_names: &PeerSetProtocolNames,
message: M,
metrics: &super::Metrics,
+ network_notification_sinks: &Arc>>>,
) where
M: Encode + Clone,
{
@@ -61,29 +63,31 @@ pub(crate) fn send_message(
encoded
};
+ let notification_sinks = network_notification_sinks.lock();
+
// optimization: avoid cloning the message for the last peer in the
// list. The message payload can be quite large. If the underlying
// network used `Bytes` this would not be necessary.
+ //
+ // peer may have gotten disconnect by the time `send_message()` is called
+ // at which point the the sink is not available.
let last_peer = peers.pop();
- // optimization: generate the protocol name once.
- let protocol_name = protocol_names.get_name(peer_set, version);
peers.into_iter().for_each(|peer| {
- net.write_notification(peer, protocol_name.clone(), message.clone());
+ if let Some(sink) = notification_sinks.get(&(peer_set, peer)) {
+ sink.send_sync_notification(message.clone());
+ }
});
+
if let Some(peer) = last_peer {
- net.write_notification(peer, protocol_name, message);
+ if let Some(sink) = notification_sinks.get(&(peer_set, peer)) {
+ sink.send_sync_notification(message.clone());
+ }
}
}
/// An abstraction over networking for the purposes of this subsystem.
#[async_trait]
pub trait Network: Clone + Send + 'static {
- /// Get a stream of all events occurring on the network. This may include events unrelated
- /// to the Polkadot protocol - the user of this function should filter only for events related
- /// to the [`VALIDATION_PROTOCOL_NAME`](VALIDATION_PROTOCOL_NAME)
- /// or [`COLLATION_PROTOCOL_NAME`](COLLATION_PROTOCOL_NAME)
- fn event_stream(&mut self) -> BoxStream<'static, NetworkEvent>;
-
/// Ask the network to keep a substream open with these nodes and not disconnect from them
/// until removed from the protocol's peer set.
/// Note that `out_peers` setting has no effect on this.
@@ -115,16 +119,12 @@ pub trait Network: Clone + Send + 'static {
/// Disconnect a given peer from the protocol specified without harming reputation.
fn disconnect_peer(&self, who: PeerId, protocol: ProtocolName);
- /// Write a notification to a peer on the given protocol.
- fn write_notification(&self, who: PeerId, protocol: ProtocolName, message: Vec);
+ /// Get peer role.
+ fn peer_role(&self, who: PeerId, handshake: Vec) -> Option;
}
#[async_trait]
impl Network for Arc> {
- fn event_stream(&mut self) -> BoxStream<'static, NetworkEvent> {
- NetworkService::event_stream(self, "polkadot-network-bridge").boxed()
- }
-
async fn set_reserved_peers(
&mut self,
protocol: ProtocolName,
@@ -149,10 +149,6 @@ impl Network for Arc> {
NetworkService::disconnect_peer(&**self, who, protocol);
}
- fn write_notification(&self, who: PeerId, protocol: ProtocolName, message: Vec) {
- NetworkService::write_notification(&**self, who, protocol, message);
- }
-
async fn start_request(
&self,
authority_discovery: &mut AD,
@@ -224,6 +220,10 @@ impl Network for Arc> {
if_disconnected,
);
}
+
+ fn peer_role(&self, who: PeerId, handshake: Vec) -> Option {
+ NetworkService::peer_role(self, who, handshake)
+ }
}
/// We assume one `peer_id` per `authority_id`.
diff --git a/node/network/bridge/src/rx/mod.rs b/node/network/bridge/src/rx/mod.rs
index 11a2dc6be83a..72ffbd9252ac 100644
--- a/node/network/bridge/src/rx/mod.rs
+++ b/node/network/bridge/src/rx/mod.rs
@@ -17,21 +17,18 @@
//! The Network Bridge Subsystem - handles _incoming_ messages from the network, forwarded to the relevant subsystems.
use super::*;
-use always_assert::never;
use bytes::Bytes;
-use futures::stream::BoxStream;
use parity_scale_codec::{Decode, DecodeAll};
-use sc_network::Event as NetworkEvent;
+use sc_network::service::traits::{
+ MessageSink, NotificationEvent, NotificationService, ValidationResult,
+};
use sp_consensus::SyncOracle;
use polkadot_node_network_protocol::{
self as net_protocol,
grid_topology::{SessionGridTopology, TopologyPeerInfo},
- peer_set::{
- CollationVersion, PeerSet, PeerSetProtocolNames, PerPeerSet, ProtocolVersion,
- ValidationVersion,
- },
+ peer_set::{CollationVersion, PeerSet, PeerSetProtocolNames, ValidationVersion},
v1 as protocol_v1, ObservedRole, OurView, PeerId, UnifiedReputationChange as Rep, View,
};
@@ -83,6 +80,9 @@ pub struct NetworkBridgeRx {
shared: Shared,
metrics: Metrics,
peerset_protocol_names: PeerSetProtocolNames,
+ validation_service: Box,
+ collation_service: Box,
+ network_notification_sinks: Arc>>>,
}
impl NetworkBridgeRx {
@@ -96,8 +96,18 @@ impl NetworkBridgeRx {
sync_oracle: Box,
metrics: Metrics,
peerset_protocol_names: PeerSetProtocolNames,
+ mut notification_services: HashMap>,
+ network_notification_sinks: Arc>>>,
) -> Self {
let shared = Shared::default();
+
+ let validation_service = notification_services
+ .remove(&PeerSet::Validation)
+ .expect("validation protocol was enabled so `NotificationService` for it exists. qed");
+ let collation_service = notification_services
+ .remove(&PeerSet::Collation)
+ .expect("collation protocol was enabled so `NotificationService` for it exists. qed");
+
Self {
network_service,
authority_discovery_service,
@@ -105,6 +115,9 @@ impl NetworkBridgeRx {
shared,
metrics,
peerset_protocol_names,
+ validation_service,
+ collation_service,
+ network_notification_sinks,
}
}
}
@@ -115,371 +128,481 @@ where
Net: Network + Sync,
AD: validator_discovery::AuthorityDiscovery + Clone + Sync,
{
- fn start(mut self, ctx: Context) -> SpawnedSubsystem {
- // The stream of networking events has to be created at initialization, otherwise the
- // networking might open connections before the stream of events has been grabbed.
- let network_stream = self.network_service.event_stream();
-
+ fn start(self, ctx: Context) -> SpawnedSubsystem {
// Swallow error because failure is fatal to the node and we log with more precision
// within `run_network`.
- let future = run_network_in(self, ctx, network_stream)
+ let future = run_network_in(self, ctx)
.map_err(|e| SubsystemError::with_origin("network-bridge", e))
.boxed();
SpawnedSubsystem { name: "network-bridge-rx-subsystem", future }
}
}
-async fn handle_network_messages(
- mut sender: impl overseer::NetworkBridgeRxSenderTrait,
- mut network_service: impl Network,
- network_stream: BoxStream<'static, NetworkEvent>,
- mut authority_discovery_service: AD,
- metrics: Metrics,
- shared: Shared,
- peerset_protocol_names: PeerSetProtocolNames,
-) -> Result<(), Error>
+/// Handle notification event received over the validation protocol.
+async fn handle_validation_message(
+ event: NotificationEvent,
+ network_service: &mut impl Network,
+ sender: &mut impl overseer::NetworkBridgeRxSenderTrait,
+ authority_discovery_service: &mut AD,
+ metrics: &Metrics,
+ shared: &Shared,
+ peerset_protocol_names: &PeerSetProtocolNames,
+ notification_service: &mut Box,
+ network_notification_sinks: &mut Arc>>>,
+) -> Result<(), ()>
where
AD: validator_discovery::AuthorityDiscovery + Send,
{
- let mut network_stream = network_stream.fuse();
- loop {
- match network_stream.next().await {
- None => return Err(Error::EventStreamConcluded),
- Some(NetworkEvent::Dht(_)) => {},
- Some(NetworkEvent::NotificationStreamOpened {
- remote: peer,
- protocol,
- role,
- negotiated_fallback,
- received_handshake: _,
- }) => {
- let role = ObservedRole::from(role);
+ match event {
+ NotificationEvent::ValidateInboundSubstream { result_tx, .. } => {
+ let _ = result_tx.send(ValidationResult::Accept);
+ },
+ NotificationEvent::NotificationStreamOpened {
+ peer,
+ handshake,
+ negotiated_fallback,
+ ..
+ } => {
+ let role = match network_service.peer_role(peer, handshake) {
+ Some(role) => ObservedRole::from(role),
+ None => {
+ gum::debug!(
+ target: LOG_TARGET,
+ ?peer,
+ "Failed to determine peer role",
+ );
+ return Ok(())
+ },
+ };
+
+ let (peer_set, version) = {
let (peer_set, version) = {
- let (peer_set, version) =
- match peerset_protocol_names.try_get_protocol(&protocol) {
- None => continue,
- Some(p) => p,
- };
-
- if let Some(fallback) = negotiated_fallback {
- match peerset_protocol_names.try_get_protocol(&fallback) {
- None => {
+ let name = peerset_protocol_names.get_main_name(PeerSet::Validation);
+ peerset_protocol_names
+ .try_get_protocol(&name)
+ .expect("validation protocol exists since it's enabled. qed")
+ };
+
+ if let Some(fallback) = negotiated_fallback {
+ match peerset_protocol_names.try_get_protocol(&fallback) {
+ None => {
+ gum::debug!(
+ target: LOG_TARGET,
+ fallback = &*fallback,
+ ?peer,
+ ?peer_set,
+ "Unknown fallback",
+ );
+
+ return Ok(())
+ },
+ Some((p2, v2)) => {
+ if p2 != peer_set {
gum::debug!(
target: LOG_TARGET,
fallback = &*fallback,
- ?peer,
- ?peer_set,
- "Unknown fallback",
+ fallback_peerset = ?p2,
+ peerset = ?peer_set,
+ "Fallback mismatched peer-set",
);
- continue
- },
- Some((p2, v2)) => {
- if p2 != peer_set {
- gum::debug!(
- target: LOG_TARGET,
- fallback = &*fallback,
- fallback_peerset = ?p2,
- protocol = &*protocol,
- peerset = ?peer_set,
- "Fallback mismatched peer-set",
- );
-
- continue
- }
-
- (p2, v2)
- },
- }
- } else {
- (peer_set, version)
- }
- };
-
- gum::debug!(
- target: LOG_TARGET,
- action = "PeerConnected",
- peer_set = ?peer_set,
- version = %version,
- peer = ?peer,
- role = ?role
- );
-
- let local_view = {
- let mut shared = shared.0.lock();
- let peer_map = match peer_set {
- PeerSet::Validation => &mut shared.validation_peers,
- PeerSet::Collation => &mut shared.collation_peers,
- };
+ return Ok(())
+ }
- match peer_map.entry(peer) {
- hash_map::Entry::Occupied(_) => continue,
- hash_map::Entry::Vacant(vacant) => {
- vacant.insert(PeerData { view: View::default(), version });
+ (p2, v2)
},
}
-
- metrics.on_peer_connected(peer_set, version);
- metrics.note_peer_count(peer_set, version, peer_map.len());
-
- shared.local_view.clone().unwrap_or(View::default())
- };
-
- let maybe_authority =
- authority_discovery_service.get_authority_ids_by_peer_id(peer).await;
-
- match peer_set {
- PeerSet::Validation => {
- dispatch_validation_events_to_all(
- vec![
- NetworkBridgeEvent::PeerConnected(
- peer,
- role,
- version,
- maybe_authority,
- ),
- NetworkBridgeEvent::PeerViewChange(peer, View::default()),
- ],
- &mut sender,
- )
- .await;
-
- send_message(
- &mut network_service,
- vec![peer],
- PeerSet::Validation,
- version,
- &peerset_protocol_names,
- WireMessage::::ViewUpdate(local_view),
- &metrics,
- );
- },
- PeerSet::Collation => {
- dispatch_collation_events_to_all(
- vec![
- NetworkBridgeEvent::PeerConnected(
- peer,
- role,
- version,
- maybe_authority,
- ),
- NetworkBridgeEvent::PeerViewChange(peer, View::default()),
- ],
- &mut sender,
- )
- .await;
-
- send_message(
- &mut network_service,
- vec![peer],
- PeerSet::Collation,
- version,
- &peerset_protocol_names,
- WireMessage::::ViewUpdate(local_view),
- &metrics,
- );
+ } else {
+ (peer_set, version)
+ }
+ };
+
+ // store the notification sink to `network_notification_sinks` so both `NetworkBridgeRx`
+ // and `NetworkBridgeTx` can send messages to the peer.
+ match notification_service.message_sink(&peer) {
+ Some(sink) => {
+ network_notification_sinks.lock().insert((peer_set, peer), sink);
+ },
+ None => {
+ gum::warn!(
+ target: LOG_TARGET,
+ peer_set = ?peer_set,
+ version = %version,
+ peer = ?peer,
+ role = ?role,
+ "Message sink not available for peer",
+ );
+ return Ok(())
+ },
+ }
+
+ gum::debug!(
+ target: LOG_TARGET,
+ action = "PeerConnected",
+ peer_set = ?peer_set,
+ version = %version,
+ peer = ?peer,
+ role = ?role
+ );
+
+ let local_view = {
+ let mut shared = shared.0.lock();
+ let peer_map = &mut shared.validation_peers;
+
+ match peer_map.entry(peer) {
+ hash_map::Entry::Occupied(_) => return Ok(()),
+ hash_map::Entry::Vacant(vacant) => {
+ vacant.insert(PeerData { view: View::default(), version });
},
}
- },
- Some(NetworkEvent::NotificationStreamClosed { remote: peer, protocol }) => {
- let (peer_set, version) = match peerset_protocol_names.try_get_protocol(&protocol) {
- None => continue,
- Some(peer_set) => peer_set,
- };
- gum::debug!(
- target: LOG_TARGET,
- action = "PeerDisconnected",
- peer_set = ?peer_set,
- peer = ?peer
- );
+ metrics.on_peer_connected(peer_set, version);
+ metrics.note_peer_count(peer_set, version, peer_map.len());
- let was_connected = {
- let mut shared = shared.0.lock();
- let peer_map = match peer_set {
- PeerSet::Validation => &mut shared.validation_peers,
- PeerSet::Collation => &mut shared.collation_peers,
- };
-
- let w = peer_map.remove(&peer).is_some();
+ shared.local_view.clone().unwrap_or(View::default())
+ };
- metrics.on_peer_disconnected(peer_set, version);
- metrics.note_peer_count(peer_set, version, peer_map.len());
+ let maybe_authority =
+ authority_discovery_service.get_authority_ids_by_peer_id(peer).await;
- w
- };
+ // TODO: store sink to networkservice?
+ dispatch_validation_events_to_all(
+ vec![
+ NetworkBridgeEvent::PeerConnected(peer, role, version, maybe_authority),
+ NetworkBridgeEvent::PeerViewChange(peer, View::default()),
+ ],
+ sender,
+ )
+ .await;
- if was_connected && version == peer_set.get_main_version() {
- match peer_set {
- PeerSet::Validation =>
- dispatch_validation_event_to_all(
- NetworkBridgeEvent::PeerDisconnected(peer),
- &mut sender,
- )
- .await,
- PeerSet::Collation =>
- dispatch_collation_event_to_all(
- NetworkBridgeEvent::PeerDisconnected(peer),
- &mut sender,
- )
- .await,
- }
- }
- },
- Some(NetworkEvent::NotificationsReceived { remote, messages }) => {
- let expected_versions = {
- let mut versions = PerPeerSet::