// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // This file is part of Pezkuwi. // Pezkuwi 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. // Pezkuwi 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 Pezkuwi. If not, see . //! The Network Bridge Subsystem - handles _outgoing_ messages, from subsystem to the network. use super::*; use pezkuwi_node_network_protocol::{ peer_set::PeerSetProtocolNames, request_response::ReqProtocolNames, CollationProtocols, ValidationProtocols, }; use pezkuwi_node_subsystem::{ errors::SubsystemError, messages::{NetworkBridgeTxMessage, ReportPeerMessage}, overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, }; use pezkuwi_node_network_protocol::request_response::Requests; use pezsc_network::{MessageSink, ReputationChange}; use crate::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_v3, Network, }; use crate::metrics::Metrics; #[cfg(test)] mod tests; // network bridge log target const LOG_TARGET: &'static str = "teyrchain::network-bridge-tx"; /// The network bridge subsystem. pub struct NetworkBridgeTx { /// `Network` trait implementing type. network_service: N, authority_discovery_service: AD, metrics: Metrics, req_protocol_names: ReqProtocolNames, peerset_protocol_names: PeerSetProtocolNames, notification_sinks: Arc>>>, } impl NetworkBridgeTx { /// 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, metrics: Metrics, req_protocol_names: ReqProtocolNames, peerset_protocol_names: PeerSetProtocolNames, notification_sinks: Arc>>>, ) -> Self { Self { network_service, authority_discovery_service, metrics, req_protocol_names, peerset_protocol_names, notification_sinks, } } } #[overseer::subsystem(NetworkBridgeTx, error = SubsystemError, prefix = self::overseer)] impl NetworkBridgeTx where Net: Network + Sync, AD: validator_discovery::AuthorityDiscovery + Clone + Sync, { fn start(self, ctx: Context) -> SpawnedSubsystem { let future = run_network_out(self, ctx) .map_err(|e| SubsystemError::with_origin("network-bridge", e)) .boxed(); SpawnedSubsystem { name: "network-bridge-tx-subsystem", future } } } #[overseer::contextbounds(NetworkBridgeTx, prefix = self::overseer)] async fn handle_subsystem_messages( mut ctx: Context, mut network_service: N, mut authority_discovery_service: AD, metrics: Metrics, req_protocol_names: ReqProtocolNames, peerset_protocol_names: PeerSetProtocolNames, notification_sinks: Arc>>>, ) -> Result<(), Error> where N: Network, AD: validator_discovery::AuthorityDiscovery + Clone, { let mut validator_discovery = validator_discovery::Service::::new(peerset_protocol_names.clone()); loop { match ctx.recv().fuse().await? { FromOrchestra::Signal(OverseerSignal::Conclude) => return Ok(()), FromOrchestra::Signal(_) => { /* handled by incoming */ }, FromOrchestra::Communication { msg } => { (network_service, authority_discovery_service) = handle_incoming_subsystem_communication( &mut ctx, network_service, &mut validator_discovery, authority_discovery_service.clone(), msg, &metrics, &req_protocol_names, &peerset_protocol_names, ¬ification_sinks, ) .await; }, } } } #[overseer::contextbounds(NetworkBridgeTx, prefix = self::overseer)] async fn handle_incoming_subsystem_communication( _ctx: &mut Context, network_service: N, validator_discovery: &mut validator_discovery::Service, mut authority_discovery_service: AD, msg: NetworkBridgeTxMessage, metrics: &Metrics, req_protocol_names: &ReqProtocolNames, peerset_protocol_names: &PeerSetProtocolNames, notification_sinks: &Arc>>>, ) -> (N, AD) where N: Network, AD: validator_discovery::AuthorityDiscovery + Clone, { match msg { NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(peer, rep)) => { if !rep.value.is_positive() { gum::debug!(target: LOG_TARGET, ?peer, ?rep, action = "ReportPeer"); } metrics.on_report_event(); network_service.report_peer(peer, rep); }, NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Batch(batch)) => { for (peer, score) in batch { let rep = ReputationChange::new(score, "Aggregated reputation change"); if !rep.value.is_positive() { gum::debug!(target: LOG_TARGET, ?peer, ?rep, action = "ReportPeer"); } metrics.on_report_event(); network_service.report_peer(peer, rep); } }, NetworkBridgeTxMessage::DisconnectPeers(peers, peer_set) => { gum::trace!( target: LOG_TARGET, action = "DisconnectPeers", ?peers, peer_set = ?peer_set, ); // [`NetworkService`] keeps track of the protocols by their main name. let protocol = peerset_protocol_names.get_main_name(peer_set); for peer in peers { network_service.disconnect_peer(peer, protocol.clone()); } }, NetworkBridgeTxMessage::SendValidationMessage(peers, msg) => { gum::trace!( target: LOG_TARGET, action = "SendValidationMessages", ?msg, num_messages = 1usize, ); match msg { ValidationProtocols::V3(msg) => send_validation_message_v3( peers, WireMessage::ProtocolMessage(msg), &metrics, notification_sinks, ), } }, NetworkBridgeTxMessage::SendValidationMessages(msgs) => { gum::trace!( target: LOG_TARGET, action = "SendValidationMessages", num_messages = %msgs.len(), ?msgs, ); for (peers, msg) in msgs { match msg { ValidationProtocols::V3(msg) => send_validation_message_v3( peers, WireMessage::ProtocolMessage(msg), &metrics, notification_sinks, ), } } }, NetworkBridgeTxMessage::SendCollationMessage(peers, msg) => { gum::trace!( target: LOG_TARGET, action = "SendCollationMessages", num_messages = 1usize, ); match msg { CollationProtocols::V1(msg) => send_collation_message_v1( peers, WireMessage::ProtocolMessage(msg), &metrics, notification_sinks, ), CollationProtocols::V2(msg) => send_collation_message_v2( peers, WireMessage::ProtocolMessage(msg), &metrics, notification_sinks, ), } }, NetworkBridgeTxMessage::SendCollationMessages(msgs) => { gum::trace!( target: LOG_TARGET, action = "SendCollationMessages", num_messages = %msgs.len(), ); for (peers, msg) in msgs { match msg { CollationProtocols::V1(msg) => send_collation_message_v1( peers, WireMessage::ProtocolMessage(msg), &metrics, notification_sinks, ), CollationProtocols::V2(msg) => send_collation_message_v2( peers, WireMessage::ProtocolMessage(msg), &metrics, notification_sinks, ), } } }, NetworkBridgeTxMessage::SendRequests(reqs, if_disconnected) => { gum::trace!( target: LOG_TARGET, action = "SendRequests", num_requests = %reqs.len(), ); for req in reqs { match req { Requests::ChunkFetching(ref req) => { // This is not the actual request that will succeed, as we don't know yet // what that will be. It's only the primary request we tried. if req.fallback_request.is_some() { metrics.on_message("chunk_fetching_v2") } else { metrics.on_message("chunk_fetching_v1") } }, Requests::AvailableDataFetchingV1(_) => { metrics.on_message("available_data_fetching_v1") }, Requests::CollationFetchingV1(_) => metrics.on_message("collation_fetching_v1"), Requests::CollationFetchingV2(_) => metrics.on_message("collation_fetching_v2"), Requests::PoVFetchingV1(_) => metrics.on_message("pov_fetching_v1"), Requests::DisputeSendingV1(_) => metrics.on_message("dispute_sending_v1"), Requests::AttestedCandidateV2(_) => metrics.on_message("attested_candidate_v2"), } network_service .start_request( &mut authority_discovery_service, req, req_protocol_names, if_disconnected, ) .await; } }, NetworkBridgeTxMessage::ConnectToValidators { validator_ids, peer_set, failed } => { gum::trace!( target: LOG_TARGET, action = "ConnectToValidators", peer_set = ?peer_set, ids = ?validator_ids, "Received a validator connection request", ); metrics.note_desired_peer_count(peer_set, validator_ids.len()); let (network_service, ads) = validator_discovery .on_request( validator_ids, peer_set, failed, network_service, authority_discovery_service, ) .await; return (network_service, ads); }, NetworkBridgeTxMessage::ConnectToResolvedValidators { validator_addrs, peer_set } => { gum::trace!( target: LOG_TARGET, action = "ConnectToPeers", peer_set = ?peer_set, ?validator_addrs, "Received a resolved validator connection request", ); metrics.note_desired_peer_count(peer_set, validator_addrs.len()); let all_addrs = validator_addrs.into_iter().flatten().collect(); let network_service = validator_discovery .on_resolved_request(all_addrs, peer_set, network_service) .await; return (network_service, authority_discovery_service); }, NetworkBridgeTxMessage::AddToResolvedValidators { validator_addrs, peer_set } => { gum::trace!( target: LOG_TARGET, action = "AddToResolvedValidators", peer_set = ?peer_set, ?validator_addrs, "Received a resolved validator connection request", ); let all_addrs = validator_addrs.into_iter().flatten().collect(); let network_service = validator_discovery .on_add_to_resolved_request(all_addrs, peer_set, network_service) .await; return (network_service, authority_discovery_service); }, } (network_service, authority_discovery_service) } #[overseer::contextbounds(NetworkBridgeTx, prefix = self::overseer)] async fn run_network_out( bridge: NetworkBridgeTx, ctx: Context, ) -> Result<(), Error> where N: Network, AD: validator_discovery::AuthorityDiscovery + Clone + Sync, { let NetworkBridgeTx { network_service, authority_discovery_service, metrics, req_protocol_names, peerset_protocol_names, notification_sinks, } = bridge; handle_subsystem_messages( ctx, network_service, authority_discovery_service, metrics, req_protocol_names, peerset_protocol_names, notification_sinks, ) .await?; Ok(()) }