// 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 net_protocol::filter_by_peer_version;
use parity_scale_codec::{Decode, DecodeAll};
use parking_lot::Mutex;
use sc_network::{
service::traits::{NotificationEvent, ValidationResult},
MessageSink, NotificationService,
};
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, v2 as protocol_v2, vstaging as protocol_vstaging, 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_collation_message_v1, send_collation_message_v2, send_validation_message_v1,
send_validation_message_v2, send_validation_message_vstaging, Network,
};
use crate::{network::get_peer_id_by_authority_id, WireMessage};
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,
validation_service: Box,
collation_service: Box,
notification_sinks: Arc>>>,
}
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 [`peer_sets_info`].
pub fn new(
network_service: N,
authority_discovery_service: AD,
sync_oracle: Box,
metrics: Metrics,
peerset_protocol_names: PeerSetProtocolNames,
mut notification_services: HashMap>,
notification_sinks: Arc>>>,
) -> Self {
let shared = Shared::default();
let validation_service = notification_services
.remove(&PeerSet::Validation)
.expect("validation protocol was enabled so `NotificationService` must exist; qed");
let collation_service = notification_services
.remove(&PeerSet::Collation)
.expect("collation protocol was enabled so `NotificationService` must exist; qed");
Self {
network_service,
authority_discovery_service,
sync_oracle,
shared,
metrics,
peerset_protocol_names,
validation_service,
collation_service,
notification_sinks,
}
}
}
#[overseer::subsystem(NetworkBridgeRx, error = SubsystemError, prefix = self::overseer)]
impl NetworkBridgeRx
where
Net: Network + Sync,
AD: validator_discovery::AuthorityDiscovery + Clone + Sync,
{
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)
.map_err(|e| SubsystemError::with_origin("network-bridge", e))
.boxed();
SpawnedSubsystem { name: "network-bridge-rx-subsystem", future }
}
}
/// 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,
notification_sinks: &mut Arc>>>,
) where
AD: validator_discovery::AuthorityDiscovery + Send,
{
match event {
NotificationEvent::ValidateInboundSubstream { peer, handshake, result_tx, .. } => {
// only accept peers whose role can be determined
let result = network_service
.peer_role(peer, handshake)
.map_or(ValidationResult::Reject, |_| ValidationResult::Accept);
let _ = result_tx.send(result);
},
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 for validation protocol",
);
return
},
};
let (peer_set, version) = {
let (peer_set, version) =
(PeerSet::Validation, PeerSet::Validation.get_main_version());
if let Some(fallback) = negotiated_fallback {
match peerset_protocol_names.try_get_protocol(&fallback) {
None => {
gum::debug!(
target: LOG_TARGET,
fallback = &*fallback,
?peer,
peerset = ?peer_set,
"Unknown fallback",
);
return
},
Some((p2, v2)) => {
if p2 != peer_set {
gum::debug!(
target: LOG_TARGET,
fallback = &*fallback,
fallback_peerset = ?p2,
peerset = ?peer_set,
"Fallback mismatched peer-set",
);
return
}
(p2, v2)
},
}
} else {
(peer_set, version)
}
};
// store the notification sink to `notification_sinks` so both `NetworkBridgeRx`
// and `NetworkBridgeTx` can send messages to the peer.
match notification_service.message_sink(&peer) {
Some(sink) => {
notification_sinks.lock().insert((peer_set, peer), sink);
},
None => {
gum::warn!(
target: LOG_TARGET,
peerset = ?peer_set,
version = %version,
?peer,
?role,
"Message sink not available for peer",
);
return
},
}
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,
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;
dispatch_validation_events_to_all(
vec![
NetworkBridgeEvent::PeerConnected(peer, role, version, maybe_authority),
NetworkBridgeEvent::PeerViewChange(peer, View::default()),
],
sender,
&metrics,
)
.await;
match ValidationVersion::try_from(version)
.expect("try_get_protocol has already checked version is known; qed")
{
ValidationVersion::V1 => send_validation_message_v1(
vec![peer],
WireMessage::::ViewUpdate(local_view),
metrics,
notification_sinks,
),
ValidationVersion::VStaging => send_validation_message_vstaging(
vec![peer],
WireMessage::::ViewUpdate(local_view),
metrics,
notification_sinks,
),
ValidationVersion::V2 => send_validation_message_v2(
vec![peer],
WireMessage::::ViewUpdate(local_view),
metrics,
notification_sinks,
),
}
},
NotificationEvent::NotificationStreamClosed { peer } => {
let (peer_set, version) = (PeerSet::Validation, PeerSet::Validation.get_main_version());
gum::debug!(
target: LOG_TARGET,
action = "PeerDisconnected",
?peer_set,
?peer
);
let was_connected = {
let mut shared = shared.0.lock();
let peer_map = &mut shared.validation_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
};
notification_sinks.lock().remove(&(peer_set, peer));
if was_connected && version == peer_set.get_main_version() {
dispatch_validation_event_to_all(
NetworkBridgeEvent::PeerDisconnected(peer),
sender,
&metrics,
)
.await;
}
},
NotificationEvent::NotificationReceived { peer, notification } => {
let expected_versions = {
let mut versions = PerPeerSet::