// Copyright (C) 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 .
//! 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 sp_consensus::SyncOracle;
use polkadot_node_network_protocol::{
self as net_protocol,
grid_topology::{SessionGridTopology, TopologyPeerInfo},
peer_set::{
CollationVersion, PeerSet, PeerSetProtocolNames, PerPeerSet, ProtocolVersion,
ValidationVersion,
},
v1 as protocol_v1, ObservedRole, OurView, PeerId, UnifiedReputationChange as Rep, View,
};
use polkadot_node_subsystem::{
errors::SubsystemError,
messages::{
network_bridge_event::NewGossipTopology, ApprovalDistributionMessage,
BitfieldDistributionMessage, CollatorProtocolMessage, GossipSupportMessage,
NetworkBridgeEvent, NetworkBridgeRxMessage, StatementDistributionMessage,
},
overseer, ActivatedLeaf, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, SpawnedSubsystem,
};
use polkadot_primitives::{AuthorityDiscoveryId, BlockNumber, Hash, ValidatorIndex};
/// Peer set info for network initialization.
///
/// To be passed to [`FullNetworkConfiguration::add_notification_protocol`]().
pub use polkadot_node_network_protocol::peer_set::{peer_sets_info, IsAuthority};
use std::{
collections::{hash_map, HashMap},
iter::ExactSizeIterator,
};
use super::validator_discovery;
/// Actual interfacing to the network based on the `Network` trait.
///
/// Defines the `Network` trait with an implementation for an `Arc`.
use crate::network::{send_message, Network};
use crate::network::get_peer_id_by_authority_id;
use super::metrics::Metrics;
#[cfg(test)]
mod tests;
// network bridge log target
const LOG_TARGET: &'static str = "parachain::network-bridge-rx";
/// The network bridge subsystem - network receiving side.
pub struct NetworkBridgeRx {
/// `Network` trait implementing type.
network_service: N,
authority_discovery_service: AD,
sync_oracle: Box,
shared: Shared,
metrics: Metrics,
peerset_protocol_names: PeerSetProtocolNames,
}
impl NetworkBridgeRx {
/// Create a new network bridge subsystem with underlying network service and authority
/// discovery service.
///
/// 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,
sync_oracle: Box,
metrics: Metrics,
peerset_protocol_names: PeerSetProtocolNames,
) -> Self {
let shared = Shared::default();
Self {
network_service,
authority_discovery_service,
sync_oracle,
shared,
metrics,
peerset_protocol_names,
}
}
}
#[overseer::subsystem(NetworkBridgeRx, error = SubsystemError, prefix = self::overseer)]
impl NetworkBridgeRx
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();
// 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)
.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>
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);
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 => {
gum::debug!(
target: LOG_TARGET,
fallback = &*fallback,
?peer,
?peer_set,
"Unknown fallback",
);
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,
};
match peer_map.entry(peer) {
hash_map::Entry::Occupied(_) => continue,
hash_map::Entry::Vacant(vacant) => {
vacant.insert(PeerData { view: View::default(), version });
},
}
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,
);
},
}
},
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
);
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();
metrics.on_peer_disconnected(peer_set, version);
metrics.note_peer_count(peer_set, version, peer_map.len());
w
};
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::