feat: initialize Kurdistan SDK - independent fork of Polkadot SDK
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use pezkuwi_node_subsystem::SubsystemError;
|
||||
pub(crate) use pezkuwi_overseer::OverseerError;
|
||||
|
||||
#[fatality::fatality(splitable)]
|
||||
pub(crate) enum Error {
|
||||
/// Received error from overseer:
|
||||
#[fatal]
|
||||
#[error(transparent)]
|
||||
SubsystemError(#[from] SubsystemError),
|
||||
/// The stream of incoming events concluded.
|
||||
#[fatal]
|
||||
#[error("Event stream closed unexpectedly")]
|
||||
EventStreamConcluded,
|
||||
}
|
||||
|
||||
impl From<OverseerError> for Error {
|
||||
fn from(e: OverseerError) -> Self {
|
||||
Error::SubsystemError(SubsystemError::from(e))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! The Network Bridge Subsystem - protocol multiplexer for Pezkuwi.
|
||||
//!
|
||||
//! Split into incoming (`..In`) and outgoing (`..Out`) subsystems.
|
||||
|
||||
#![deny(unused_crate_dependencies)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use futures::prelude::*;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use sp_consensus::SyncOracle;
|
||||
|
||||
use pezkuwi_node_network_protocol::{
|
||||
peer_set::{PeerSet, ProtocolVersion},
|
||||
PeerId, UnifiedReputationChange as Rep, View,
|
||||
};
|
||||
|
||||
/// Peer set info for network initialization.
|
||||
///
|
||||
/// To be passed to [`FullNetworkConfiguration::add_notification_protocol`]().
|
||||
pub use pezkuwi_node_network_protocol::peer_set::{peer_sets_info, IsAuthority};
|
||||
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
mod validator_discovery;
|
||||
|
||||
/// Actual interfacing to the network based on the `Network` trait.
|
||||
///
|
||||
/// Defines the `Network` trait with an implementation for an `Arc<NetworkService>`.
|
||||
mod network;
|
||||
use self::network::Network;
|
||||
|
||||
mod metrics;
|
||||
pub use self::metrics::Metrics;
|
||||
|
||||
mod errors;
|
||||
pub(crate) use self::errors::Error;
|
||||
|
||||
mod tx;
|
||||
pub use self::tx::*;
|
||||
|
||||
mod rx;
|
||||
pub use self::rx::*;
|
||||
|
||||
/// The maximum amount of heads a peer is allowed to have in their view at any time.
|
||||
///
|
||||
/// We use the same limit to compute the view sent to peers locally.
|
||||
pub(crate) const MAX_VIEW_HEADS: usize = 5;
|
||||
|
||||
pub(crate) const MALFORMED_MESSAGE_COST: Rep = Rep::CostMajor("Malformed Network-bridge message");
|
||||
pub(crate) const UNCONNECTED_PEERSET_COST: Rep =
|
||||
Rep::CostMinor("Message sent to un-connected peer-set");
|
||||
pub(crate) const MALFORMED_VIEW_COST: Rep = Rep::CostMajor("Malformed view");
|
||||
pub(crate) const EMPTY_VIEW_COST: Rep = Rep::CostMajor("Peer sent us an empty view");
|
||||
|
||||
/// Messages from and to the network.
|
||||
///
|
||||
/// As transmitted to and received from subsystems.
|
||||
#[derive(Debug, Encode, Decode, Clone)]
|
||||
pub(crate) enum WireMessage<M> {
|
||||
/// A message from a peer on a specific protocol.
|
||||
#[codec(index = 1)]
|
||||
ProtocolMessage(M),
|
||||
/// A view update from a peer.
|
||||
#[codec(index = 2)]
|
||||
ViewUpdate(View),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PeerData {
|
||||
/// The Latest view sent by the peer.
|
||||
view: View,
|
||||
version: ProtocolVersion,
|
||||
}
|
||||
|
||||
/// Shared state between incoming and outgoing.
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub(crate) struct Shared(Arc<Mutex<SharedInner>>);
|
||||
|
||||
#[derive(Default)]
|
||||
struct SharedInner {
|
||||
local_view: Option<View>,
|
||||
validation_peers: HashMap<PeerId, PeerData>,
|
||||
collation_peers: HashMap<PeerId, PeerData>,
|
||||
}
|
||||
|
||||
// Counts the number of peers that are connectioned using `version`
|
||||
fn count_peers_by_version(peers: &HashMap<PeerId, PeerData>) -> HashMap<ProtocolVersion, usize> {
|
||||
let mut by_version_count = HashMap::new();
|
||||
for peer in peers.values() {
|
||||
*(by_version_count.entry(peer.version).or_default()) += 1;
|
||||
}
|
||||
by_version_count
|
||||
}
|
||||
|
||||
// Notes the peer count
|
||||
fn note_peers_count(metrics: &Metrics, shared: &Shared) {
|
||||
let guard = shared.0.lock();
|
||||
let validation_stats = count_peers_by_version(&guard.validation_peers);
|
||||
let collation_stats = count_peers_by_version(&guard.collation_peers);
|
||||
|
||||
for (version, count) in validation_stats {
|
||||
metrics.note_peer_count(PeerSet::Validation, version, count)
|
||||
}
|
||||
|
||||
for (version, count) in collation_stats {
|
||||
metrics.note_peer_count(PeerSet::Collation, version, count)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum Mode {
|
||||
Syncing(Box<dyn SyncOracle + Send>),
|
||||
Active,
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::{PeerSet, ProtocolVersion};
|
||||
use pezkuwi_node_metrics::metrics::{self, prometheus};
|
||||
|
||||
/// Metrics for the network bridge.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Metrics(pub(crate) Option<MetricsInner>);
|
||||
|
||||
fn peer_set_label(peer_set: PeerSet, version: ProtocolVersion) -> &'static str {
|
||||
// Higher level code is meant to protect against this ever happening.
|
||||
peer_set.get_protocol_label(version).unwrap_or("<internal error>")
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl Metrics {
|
||||
pub fn on_peer_connected(&self, peer_set: PeerSet, version: ProtocolVersion) {
|
||||
self.0.as_ref().map(|metrics| {
|
||||
metrics
|
||||
.connected_events
|
||||
.with_label_values(&[peer_set_label(peer_set, version)])
|
||||
.inc()
|
||||
});
|
||||
}
|
||||
|
||||
pub fn on_peer_disconnected(&self, peer_set: PeerSet, version: ProtocolVersion) {
|
||||
self.0.as_ref().map(|metrics| {
|
||||
metrics
|
||||
.disconnected_events
|
||||
.with_label_values(&[peer_set_label(peer_set, version)])
|
||||
.inc()
|
||||
});
|
||||
}
|
||||
|
||||
pub fn note_peer_count(&self, peer_set: PeerSet, version: ProtocolVersion, count: usize) {
|
||||
if let Some(metrics) = self.0.as_ref() {
|
||||
let label = peer_set_label(peer_set, version);
|
||||
metrics.peer_count.with_label_values(&[label]).set(count as u64);
|
||||
metrics.peer_connectivity.with_label_values(&[label]).observe(count as f64);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_notification_received(
|
||||
&self,
|
||||
peer_set: PeerSet,
|
||||
version: ProtocolVersion,
|
||||
size: usize,
|
||||
) {
|
||||
if let Some(metrics) = self.0.as_ref() {
|
||||
metrics
|
||||
.notifications_received
|
||||
.with_label_values(&[peer_set_label(peer_set, version)])
|
||||
.inc();
|
||||
|
||||
metrics
|
||||
.bytes_received
|
||||
.with_label_values(&[peer_set_label(peer_set, version)])
|
||||
.inc_by(size as u64);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_notification_sent(
|
||||
&self,
|
||||
peer_set: PeerSet,
|
||||
version: ProtocolVersion,
|
||||
size: usize,
|
||||
to_peers: usize,
|
||||
) {
|
||||
if let Some(metrics) = self.0.as_ref() {
|
||||
metrics
|
||||
.notifications_sent
|
||||
.with_label_values(&[peer_set_label(peer_set, version)])
|
||||
.inc_by(to_peers as u64);
|
||||
|
||||
metrics
|
||||
.bytes_sent
|
||||
.with_label_values(&[peer_set_label(peer_set, version)])
|
||||
.inc_by((size * to_peers) as u64);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn note_desired_peer_count(&self, peer_set: PeerSet, size: usize) {
|
||||
self.0.as_ref().map(|metrics| {
|
||||
metrics
|
||||
.desired_peer_count
|
||||
.with_label_values(&[peer_set.get_label()])
|
||||
.set(size as u64)
|
||||
});
|
||||
}
|
||||
|
||||
pub fn on_report_event(&self) {
|
||||
if let Some(metrics) = self.0.as_ref() {
|
||||
self.on_message("report_peer");
|
||||
metrics.report_events.inc()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_message(&self, message_type: &'static str) {
|
||||
if let Some(metrics) = self.0.as_ref() {
|
||||
metrics.messages_sent.with_label_values(&[message_type]).inc()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_delayed_rx_queue(&self, queue_size: usize) {
|
||||
if let Some(metrics) = self.0.as_ref() {
|
||||
metrics.rx_delayed_processing.observe(queue_size as f64);
|
||||
}
|
||||
}
|
||||
pub fn time_delayed_rx_events(
|
||||
&self,
|
||||
) -> Option<metrics::prometheus::prometheus::HistogramTimer> {
|
||||
self.0.as_ref().map(|metrics| metrics.rx_delayed_processing_time.start_timer())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct MetricsInner {
|
||||
peer_count: prometheus::GaugeVec<prometheus::U64>,
|
||||
peer_connectivity: prometheus::HistogramVec,
|
||||
connected_events: prometheus::CounterVec<prometheus::U64>,
|
||||
disconnected_events: prometheus::CounterVec<prometheus::U64>,
|
||||
desired_peer_count: prometheus::GaugeVec<prometheus::U64>,
|
||||
report_events: prometheus::Counter<prometheus::U64>,
|
||||
|
||||
notifications_received: prometheus::CounterVec<prometheus::U64>,
|
||||
notifications_sent: prometheus::CounterVec<prometheus::U64>,
|
||||
|
||||
bytes_received: prometheus::CounterVec<prometheus::U64>,
|
||||
bytes_sent: prometheus::CounterVec<prometheus::U64>,
|
||||
|
||||
messages_sent: prometheus::CounterVec<prometheus::U64>,
|
||||
// The reason why a `Histogram` is used to track a queue size is that
|
||||
// we need not only an average size of the queue (that will be 0 normally), but
|
||||
// we also need a dynamics for this queue size in case of messages delays.
|
||||
rx_delayed_processing: prometheus::Histogram,
|
||||
rx_delayed_processing_time: prometheus::Histogram,
|
||||
}
|
||||
|
||||
impl metrics::Metrics for Metrics {
|
||||
fn try_register(
|
||||
registry: &prometheus::Registry,
|
||||
) -> std::result::Result<Self, prometheus::PrometheusError> {
|
||||
let metrics = MetricsInner {
|
||||
peer_count: prometheus::register(
|
||||
prometheus::GaugeVec::new(
|
||||
prometheus::Opts::new(
|
||||
"pezkuwi_teyrchain_peer_count",
|
||||
"The number of peers on a teyrchain-related peer-set",
|
||||
),
|
||||
&["protocol"]
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
peer_connectivity: prometheus::register(
|
||||
prometheus::HistogramVec::new(
|
||||
prometheus::HistogramOpts::new(
|
||||
"pezkuwi_teyrchain_peer_connectivity",
|
||||
"Histogram of peer counts on a teyrchain-related peer-set to track connectivity patterns",
|
||||
).buckets(vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 10.0, 15.0, 20.0, 25.0, 30.0, 40.0, 50.0, 100.0, 250.0, 500.0, 1000.0]),
|
||||
&["protocol"]
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
connected_events: prometheus::register(
|
||||
prometheus::CounterVec::new(
|
||||
prometheus::Opts::new(
|
||||
"pezkuwi_teyrchain_peer_connect_events_total",
|
||||
"The number of peer connect events on a teyrchain notifications protocol",
|
||||
),
|
||||
&["protocol"]
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
disconnected_events: prometheus::register(
|
||||
prometheus::CounterVec::new(
|
||||
prometheus::Opts::new(
|
||||
"pezkuwi_teyrchain_peer_disconnect_events_total",
|
||||
"The number of peer disconnect events on a teyrchain notifications protocol",
|
||||
),
|
||||
&["protocol"]
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
desired_peer_count: prometheus::register(
|
||||
prometheus::GaugeVec::new(
|
||||
prometheus::Opts::new(
|
||||
"pezkuwi_teyrchain_desired_peer_count",
|
||||
"The number of peers that the local node is expected to connect to on a teyrchain-related peer-set (either including or not including unresolvable authorities, depending on whether `ConnectToValidators` or `ConnectToValidatorsResolved` was used.)",
|
||||
),
|
||||
&["protocol"]
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
report_events: prometheus::register(
|
||||
prometheus::Counter::new(
|
||||
"pezkuwi_teyrchain_network_report_events_total",
|
||||
"The amount of reputation changes issued by subsystems",
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
notifications_received: prometheus::register(
|
||||
prometheus::CounterVec::new(
|
||||
prometheus::Opts::new(
|
||||
"pezkuwi_teyrchain_notifications_received_total",
|
||||
"The number of notifications received on a teyrchain protocol",
|
||||
),
|
||||
&["protocol"]
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
notifications_sent: prometheus::register(
|
||||
prometheus::CounterVec::new(
|
||||
prometheus::Opts::new(
|
||||
"pezkuwi_teyrchain_notifications_sent_total",
|
||||
"The number of notifications sent on a teyrchain protocol",
|
||||
),
|
||||
&["protocol"]
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
bytes_received: prometheus::register(
|
||||
prometheus::CounterVec::new(
|
||||
prometheus::Opts::new(
|
||||
"pezkuwi_teyrchain_notification_bytes_received_total",
|
||||
"The number of bytes received on a teyrchain notification protocol",
|
||||
),
|
||||
&["protocol"]
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
bytes_sent: prometheus::register(
|
||||
prometheus::CounterVec::new(
|
||||
prometheus::Opts::new(
|
||||
"pezkuwi_teyrchain_notification_bytes_sent_total",
|
||||
"The number of bytes sent on a teyrchain notification protocol",
|
||||
),
|
||||
&["protocol"]
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
messages_sent: prometheus::register(
|
||||
prometheus::CounterVec::new(
|
||||
prometheus::Opts::new(
|
||||
"pezkuwi_teyrchain_messages_sent_total",
|
||||
"The number of messages sent via network bridge",
|
||||
),
|
||||
&["type"]
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
rx_delayed_processing: prometheus::register(
|
||||
prometheus::Histogram::with_opts(
|
||||
prometheus::HistogramOpts::new(
|
||||
"pezkuwi_teyrchain_network_bridge_rx_delayed",
|
||||
"Number of events being delayed while broadcasting from the network bridge",
|
||||
).buckets(vec![0.0, 1.0, 2.0, 8.0, 16.0]),
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
rx_delayed_processing_time: prometheus::register(
|
||||
prometheus::Histogram::with_opts(
|
||||
prometheus::HistogramOpts::new(
|
||||
"pezkuwi_teyrchain_network_bridge_rx_delayed_time",
|
||||
"Time spent for waiting of the delayed events",
|
||||
),
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
};
|
||||
|
||||
Ok(Metrics(Some(metrics)))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use codec::Encode;
|
||||
|
||||
use sc_network::{
|
||||
config::parse_addr, multiaddr::Multiaddr, service::traits::NetworkService, types::ProtocolName,
|
||||
IfDisconnected, MessageSink, OutboundFailure, ReputationChange, RequestFailure,
|
||||
};
|
||||
|
||||
use pezkuwi_node_network_protocol::{
|
||||
peer_set::{CollationVersion, PeerSet, ProtocolVersion, ValidationVersion},
|
||||
request_response::{OutgoingRequest, Recipient, ReqProtocolNames, Requests},
|
||||
v1 as protocol_v1, v2 as protocol_v2, v3 as protocol_v3, PeerId,
|
||||
};
|
||||
use pezkuwi_primitives::AuthorityDiscoveryId;
|
||||
|
||||
use crate::{metrics::Metrics, validator_discovery::AuthorityDiscovery, WireMessage};
|
||||
|
||||
// network bridge network abstraction log target
|
||||
const LOG_TARGET: &'static str = "teyrchain::network-bridge-net";
|
||||
|
||||
// Helper function to send a validation v3 message to a list of peers.
|
||||
// Messages are always sent via the main protocol, even legacy protocol messages.
|
||||
pub(crate) fn send_validation_message_v3(
|
||||
peers: Vec<PeerId>,
|
||||
message: WireMessage<protocol_v3::ValidationProtocol>,
|
||||
metrics: &Metrics,
|
||||
notification_sinks: &Arc<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
|
||||
) {
|
||||
gum::trace!(target: LOG_TARGET, ?peers, ?message, "Sending validation v3 message to peers",);
|
||||
|
||||
send_message(
|
||||
peers,
|
||||
PeerSet::Validation,
|
||||
ValidationVersion::V3.into(),
|
||||
message,
|
||||
metrics,
|
||||
notification_sinks,
|
||||
);
|
||||
}
|
||||
|
||||
// Helper function to send a collation v1 message to a list of peers.
|
||||
// Messages are always sent via the main protocol, even legacy protocol messages.
|
||||
pub(crate) fn send_collation_message_v1(
|
||||
peers: Vec<PeerId>,
|
||||
message: WireMessage<protocol_v1::CollationProtocol>,
|
||||
metrics: &Metrics,
|
||||
notification_sinks: &Arc<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
|
||||
) {
|
||||
send_message(
|
||||
peers,
|
||||
PeerSet::Collation,
|
||||
CollationVersion::V1.into(),
|
||||
message,
|
||||
metrics,
|
||||
notification_sinks,
|
||||
);
|
||||
}
|
||||
|
||||
// Helper function to send a collation v2 message to a list of peers.
|
||||
// Messages are always sent via the main protocol, even legacy protocol messages.
|
||||
pub(crate) fn send_collation_message_v2(
|
||||
peers: Vec<PeerId>,
|
||||
message: WireMessage<protocol_v2::CollationProtocol>,
|
||||
metrics: &Metrics,
|
||||
notification_sinks: &Arc<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
|
||||
) {
|
||||
send_message(
|
||||
peers,
|
||||
PeerSet::Collation,
|
||||
CollationVersion::V2.into(),
|
||||
message,
|
||||
metrics,
|
||||
notification_sinks,
|
||||
);
|
||||
}
|
||||
|
||||
/// Lower level function that sends a message to the network using the main protocol version.
|
||||
///
|
||||
/// This function is only used internally by the network-bridge, which is responsible to only send
|
||||
/// 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.
|
||||
fn send_message<M>(
|
||||
mut peers: Vec<PeerId>,
|
||||
peer_set: PeerSet,
|
||||
version: ProtocolVersion,
|
||||
message: M,
|
||||
metrics: &super::Metrics,
|
||||
network_notification_sinks: &Arc<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
|
||||
) where
|
||||
M: Encode + Clone,
|
||||
{
|
||||
if peers.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let message = {
|
||||
let encoded = message.encode();
|
||||
metrics.on_notification_sent(peer_set, version, encoded.len(), peers.len());
|
||||
metrics.on_message(std::any::type_name::<M>());
|
||||
encoded
|
||||
};
|
||||
|
||||
let notification_sinks = network_notification_sinks.lock();
|
||||
|
||||
gum::trace!(
|
||||
target: LOG_TARGET,
|
||||
?peers,
|
||||
?peer_set,
|
||||
?version,
|
||||
?message,
|
||||
"Sending message to peers",
|
||||
);
|
||||
|
||||
// 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 sink is not available.
|
||||
let last_peer = peers.pop();
|
||||
peers.into_iter().for_each(|peer| {
|
||||
if let Some(sink) = notification_sinks.get(&(peer_set, peer)) {
|
||||
sink.send_sync_notification(message.clone());
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(peer) = last_peer {
|
||||
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 {
|
||||
/// 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.
|
||||
async fn set_reserved_peers(
|
||||
&mut self,
|
||||
protocol: ProtocolName,
|
||||
multiaddresses: HashSet<Multiaddr>,
|
||||
) -> Result<(), String>;
|
||||
|
||||
/// Ask the network to extend the reserved set with these nodes.
|
||||
async fn add_peers_to_reserved_set(
|
||||
&mut self,
|
||||
protocol: ProtocolName,
|
||||
multiaddresses: HashSet<Multiaddr>,
|
||||
) -> Result<(), String>;
|
||||
|
||||
/// Removes the peers for the protocol's peer set (both reserved and non-reserved).
|
||||
async fn remove_from_peers_set(
|
||||
&mut self,
|
||||
protocol: ProtocolName,
|
||||
peers: Vec<PeerId>,
|
||||
) -> Result<(), String>;
|
||||
|
||||
/// Send a request to a remote peer.
|
||||
async fn start_request<AD: AuthorityDiscovery>(
|
||||
&self,
|
||||
authority_discovery: &mut AD,
|
||||
req: Requests,
|
||||
req_protocol_names: &ReqProtocolNames,
|
||||
if_disconnected: IfDisconnected,
|
||||
);
|
||||
|
||||
/// Report a given peer as either beneficial (+) or costly (-) according to the given scalar.
|
||||
fn report_peer(&self, who: PeerId, rep: ReputationChange);
|
||||
|
||||
/// Disconnect a given peer from the protocol specified without harming reputation.
|
||||
fn disconnect_peer(&self, who: PeerId, protocol: ProtocolName);
|
||||
|
||||
/// Get peer role.
|
||||
fn peer_role(&self, who: PeerId, handshake: Vec<u8>) -> Option<sc_network::ObservedRole>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Network for Arc<dyn NetworkService> {
|
||||
async fn set_reserved_peers(
|
||||
&mut self,
|
||||
protocol: ProtocolName,
|
||||
multiaddresses: HashSet<Multiaddr>,
|
||||
) -> Result<(), String> {
|
||||
<dyn NetworkService>::set_reserved_peers(&**self, protocol, multiaddresses)
|
||||
}
|
||||
|
||||
async fn add_peers_to_reserved_set(
|
||||
&mut self,
|
||||
protocol: ProtocolName,
|
||||
multiaddresses: HashSet<Multiaddr>,
|
||||
) -> Result<(), String> {
|
||||
<dyn NetworkService>::add_peers_to_reserved_set(&**self, protocol, multiaddresses)
|
||||
}
|
||||
|
||||
async fn remove_from_peers_set(
|
||||
&mut self,
|
||||
protocol: ProtocolName,
|
||||
peers: Vec<PeerId>,
|
||||
) -> Result<(), String> {
|
||||
<dyn NetworkService>::remove_peers_from_reserved_set(&**self, protocol, peers)
|
||||
}
|
||||
|
||||
fn report_peer(&self, who: PeerId, rep: ReputationChange) {
|
||||
<dyn NetworkService>::report_peer(&**self, who, rep);
|
||||
}
|
||||
|
||||
fn disconnect_peer(&self, who: PeerId, protocol: ProtocolName) {
|
||||
<dyn NetworkService>::disconnect_peer(&**self, who, protocol);
|
||||
}
|
||||
|
||||
async fn start_request<AD: AuthorityDiscovery>(
|
||||
&self,
|
||||
authority_discovery: &mut AD,
|
||||
req: Requests,
|
||||
req_protocol_names: &ReqProtocolNames,
|
||||
if_disconnected: IfDisconnected,
|
||||
) {
|
||||
let (protocol, OutgoingRequest { peer, payload, pending_response, fallback_request }) =
|
||||
req.encode_request();
|
||||
|
||||
let peer_id = match peer {
|
||||
Recipient::Peer(peer_id) => Some(peer_id),
|
||||
Recipient::Authority(authority) => {
|
||||
gum::trace!(
|
||||
target: LOG_TARGET,
|
||||
?authority,
|
||||
"Searching for peer id to connect to authority",
|
||||
);
|
||||
|
||||
let mut found_peer_id = None;
|
||||
// Note: `get_addresses_by_authority_id` searched in a cache, and it thus expected
|
||||
// to be very quick.
|
||||
for addr in authority_discovery
|
||||
.get_addresses_by_authority_id(authority)
|
||||
.await
|
||||
.into_iter()
|
||||
.flat_map(|list| list.into_iter())
|
||||
{
|
||||
let (peer_id, addr) = match parse_addr(addr) {
|
||||
Ok(v) => v,
|
||||
Err(_) => continue,
|
||||
};
|
||||
<dyn NetworkService>::add_known_address(&**self, peer_id, addr);
|
||||
found_peer_id = Some(peer_id);
|
||||
}
|
||||
found_peer_id
|
||||
},
|
||||
};
|
||||
|
||||
let peer_id = match peer_id {
|
||||
None => {
|
||||
gum::debug!(target: LOG_TARGET, "Discovering authority failed");
|
||||
match pending_response
|
||||
.send(Err(RequestFailure::Network(OutboundFailure::DialFailure)))
|
||||
{
|
||||
Err(_) => {
|
||||
gum::debug!(target: LOG_TARGET, "Sending failed request response failed.")
|
||||
},
|
||||
Ok(_) => {},
|
||||
}
|
||||
return;
|
||||
},
|
||||
Some(peer_id) => peer_id,
|
||||
};
|
||||
|
||||
gum::trace!(
|
||||
target: LOG_TARGET,
|
||||
%peer_id,
|
||||
protocol = %req_protocol_names.get_name(protocol),
|
||||
fallback_protocol = ?fallback_request.as_ref().map(|(_, p)| req_protocol_names.get_name(*p)),
|
||||
?if_disconnected,
|
||||
"Starting request",
|
||||
);
|
||||
|
||||
<dyn NetworkService>::start_request(
|
||||
&**self,
|
||||
peer_id,
|
||||
req_protocol_names.get_name(protocol),
|
||||
payload,
|
||||
fallback_request.map(|(r, p)| (r, req_protocol_names.get_name(p))),
|
||||
pending_response,
|
||||
if_disconnected,
|
||||
);
|
||||
}
|
||||
|
||||
fn peer_role(&self, who: PeerId, handshake: Vec<u8>) -> Option<sc_network::ObservedRole> {
|
||||
<dyn NetworkService>::peer_role(&**self, who, handshake)
|
||||
}
|
||||
}
|
||||
|
||||
/// We assume one `peer_id` per `authority_id`.
|
||||
pub async fn get_peer_id_by_authority_id<AD: AuthorityDiscovery>(
|
||||
authority_discovery: &mut AD,
|
||||
authority: AuthorityDiscoveryId,
|
||||
) -> Option<PeerId> {
|
||||
// Note: `get_addresses_by_authority_id` searched in a cache, and it thus expected
|
||||
// to be very quick.
|
||||
authority_discovery
|
||||
.get_addresses_by_authority_id(authority)
|
||||
.await
|
||||
.into_iter()
|
||||
.flat_map(|list| list.into_iter())
|
||||
.find_map(|addr| parse_addr(addr).ok().map(|(p, _)| p))
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,400 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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 sc_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<NetworkService>`.
|
||||
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<N, AD> {
|
||||
/// `Network` trait implementing type.
|
||||
network_service: N,
|
||||
authority_discovery_service: AD,
|
||||
metrics: Metrics,
|
||||
req_protocol_names: ReqProtocolNames,
|
||||
peerset_protocol_names: PeerSetProtocolNames,
|
||||
notification_sinks: Arc<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
|
||||
}
|
||||
|
||||
impl<N, AD> NetworkBridgeTx<N, AD> {
|
||||
/// 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<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
|
||||
) -> 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<Net, AD, Context> NetworkBridgeTx<Net, AD>
|
||||
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<Context, N, AD>(
|
||||
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<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
N: Network,
|
||||
AD: validator_discovery::AuthorityDiscovery + Clone,
|
||||
{
|
||||
let mut validator_discovery =
|
||||
validator_discovery::Service::<N, AD>::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<Context, N, AD>(
|
||||
_ctx: &mut Context,
|
||||
network_service: N,
|
||||
validator_discovery: &mut validator_discovery::Service<N, AD>,
|
||||
mut authority_discovery_service: AD,
|
||||
msg: NetworkBridgeTxMessage,
|
||||
metrics: &Metrics,
|
||||
req_protocol_names: &ReqProtocolNames,
|
||||
peerset_protocol_names: &PeerSetProtocolNames,
|
||||
notification_sinks: &Arc<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
|
||||
) -> (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<N, AD, Context>(
|
||||
bridge: NetworkBridgeTx<N, AD>,
|
||||
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(())
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use futures::executor;
|
||||
use pezkuwi_node_subsystem_util::TimeoutExt;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use sc_network::{
|
||||
IfDisconnected, ObservedRole as SubstrateObservedRole, ProtocolName, ReputationChange, Roles,
|
||||
};
|
||||
|
||||
use codec::DecodeAll;
|
||||
use pezkuwi_node_network_protocol::{
|
||||
peer_set::PeerSetProtocolNames,
|
||||
request_response::{outgoing::Requests, ReqProtocolNames},
|
||||
v1 as protocol_v1, v3 as protocol_v3, CollationProtocols, ObservedRole, ValidationProtocols,
|
||||
};
|
||||
use pezkuwi_node_subsystem::{FromOrchestra, OverseerSignal};
|
||||
use pezkuwi_node_subsystem_test_helpers::TestSubsystemContextHandle;
|
||||
use pezkuwi_node_subsystem_util::metered;
|
||||
use pezkuwi_primitives::{AuthorityDiscoveryId, Hash};
|
||||
use pezkuwi_primitives_test_helpers::dummy_collator_signature;
|
||||
use sc_network::Multiaddr;
|
||||
use sp_keyring::Sr25519Keyring;
|
||||
|
||||
const TIMEOUT: std::time::Duration = pezkuwi_node_subsystem_test_helpers::TestSubsystemContextHandle::<NetworkBridgeTxMessage>::TIMEOUT;
|
||||
|
||||
use crate::{network::Network, validator_discovery::AuthorityDiscovery};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum NetworkAction {
|
||||
/// Note a change in reputation for a peer.
|
||||
ReputationChange(PeerId, ReputationChange),
|
||||
/// Disconnect a peer from the given peer-set.
|
||||
DisconnectPeer(PeerId, PeerSet),
|
||||
/// Write a notification to a given peer on the given peer-set.
|
||||
WriteNotification(PeerId, PeerSet, Vec<u8>),
|
||||
}
|
||||
|
||||
// The subsystem's view of the network.
|
||||
#[derive(Clone)]
|
||||
struct TestNetwork {
|
||||
action_tx: Arc<Mutex<metered::UnboundedMeteredSender<NetworkAction>>>,
|
||||
peerset_protocol_names: Arc<PeerSetProtocolNames>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct TestAuthorityDiscovery;
|
||||
|
||||
// The test's view of the network. This receives updates from the subsystem in the form
|
||||
// of `NetworkAction`s.
|
||||
struct TestNetworkHandle {
|
||||
action_rx: metered::UnboundedMeteredReceiver<NetworkAction>,
|
||||
_peerset_protocol_names: PeerSetProtocolNames,
|
||||
notification_sinks: Arc<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
|
||||
action_tx: Arc<Mutex<metered::UnboundedMeteredSender<NetworkAction>>>,
|
||||
}
|
||||
|
||||
struct TestMessageSink {
|
||||
peer: PeerId,
|
||||
peer_set: PeerSet,
|
||||
action_tx: Arc<Mutex<metered::UnboundedMeteredSender<NetworkAction>>>,
|
||||
}
|
||||
|
||||
impl TestMessageSink {
|
||||
fn new(
|
||||
peer: PeerId,
|
||||
peer_set: PeerSet,
|
||||
action_tx: Arc<Mutex<metered::UnboundedMeteredSender<NetworkAction>>>,
|
||||
) -> TestMessageSink {
|
||||
Self { peer, peer_set, action_tx }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MessageSink for TestMessageSink {
|
||||
fn send_sync_notification(&self, notification: Vec<u8>) {
|
||||
self.action_tx
|
||||
.lock()
|
||||
.unbounded_send(NetworkAction::WriteNotification(
|
||||
self.peer,
|
||||
self.peer_set,
|
||||
notification,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
async fn send_async_notification(
|
||||
&self,
|
||||
_notification: Vec<u8>,
|
||||
) -> Result<(), sc_network::error::Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
fn new_test_network(
|
||||
peerset_protocol_names: PeerSetProtocolNames,
|
||||
) -> (
|
||||
TestNetwork,
|
||||
TestNetworkHandle,
|
||||
TestAuthorityDiscovery,
|
||||
Arc<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
|
||||
) {
|
||||
let (action_tx, action_rx) = metered::unbounded();
|
||||
let notification_sinks = Arc::new(Mutex::new(HashMap::new()));
|
||||
let action_tx = Arc::new(Mutex::new(action_tx));
|
||||
|
||||
(
|
||||
TestNetwork {
|
||||
action_tx: action_tx.clone(),
|
||||
peerset_protocol_names: Arc::new(peerset_protocol_names.clone()),
|
||||
},
|
||||
TestNetworkHandle {
|
||||
action_rx,
|
||||
_peerset_protocol_names: peerset_protocol_names,
|
||||
action_tx,
|
||||
notification_sinks: notification_sinks.clone(),
|
||||
},
|
||||
TestAuthorityDiscovery,
|
||||
notification_sinks,
|
||||
)
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Network for TestNetwork {
|
||||
async fn set_reserved_peers(
|
||||
&mut self,
|
||||
_protocol: ProtocolName,
|
||||
_: HashSet<Multiaddr>,
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_peers_to_reserved_set(
|
||||
&mut self,
|
||||
_protocol: ProtocolName,
|
||||
_: HashSet<Multiaddr>,
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_from_peers_set(
|
||||
&mut self,
|
||||
_protocol: ProtocolName,
|
||||
_: Vec<PeerId>,
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn start_request<AD: AuthorityDiscovery>(
|
||||
&self,
|
||||
_: &mut AD,
|
||||
_: Requests,
|
||||
_: &ReqProtocolNames,
|
||||
_: IfDisconnected,
|
||||
) {
|
||||
}
|
||||
|
||||
fn report_peer(&self, who: PeerId, rep: ReputationChange) {
|
||||
self.action_tx
|
||||
.lock()
|
||||
.unbounded_send(NetworkAction::ReputationChange(who, rep))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn disconnect_peer(&self, who: PeerId, protocol: ProtocolName) {
|
||||
let (peer_set, version) = self.peerset_protocol_names.try_get_protocol(&protocol).unwrap();
|
||||
assert_eq!(version, peer_set.get_main_version());
|
||||
|
||||
self.action_tx
|
||||
.lock()
|
||||
.unbounded_send(NetworkAction::DisconnectPeer(who, peer_set))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn peer_role(&self, _peer_id: PeerId, handshake: Vec<u8>) -> Option<SubstrateObservedRole> {
|
||||
Roles::decode_all(&mut &handshake[..])
|
||||
.ok()
|
||||
.and_then(|role| Some(SubstrateObservedRole::from(role)))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl validator_discovery::AuthorityDiscovery for TestAuthorityDiscovery {
|
||||
async fn get_addresses_by_authority_id(
|
||||
&mut self,
|
||||
_authority: AuthorityDiscoveryId,
|
||||
) -> Option<HashSet<Multiaddr>> {
|
||||
None
|
||||
}
|
||||
|
||||
async fn get_authority_ids_by_peer_id(
|
||||
&mut self,
|
||||
_peer_id: PeerId,
|
||||
) -> Option<HashSet<AuthorityDiscoveryId>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl TestNetworkHandle {
|
||||
// Get the next network action.
|
||||
async fn next_network_action(&mut self) -> NetworkAction {
|
||||
self.action_rx.next().await.expect("subsystem concluded early")
|
||||
}
|
||||
|
||||
async fn connect_peer(&mut self, peer: PeerId, peer_set: PeerSet, _role: ObservedRole) {
|
||||
self.notification_sinks.lock().insert(
|
||||
(peer_set, peer),
|
||||
Box::new(TestMessageSink::new(peer, peer_set, self.action_tx.clone())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
type VirtualOverseer = TestSubsystemContextHandle<NetworkBridgeTxMessage>;
|
||||
|
||||
struct TestHarness {
|
||||
network_handle: TestNetworkHandle,
|
||||
virtual_overseer: VirtualOverseer,
|
||||
}
|
||||
|
||||
fn test_harness<T: Future<Output = VirtualOverseer>>(test: impl FnOnce(TestHarness) -> T) {
|
||||
let genesis_hash = Hash::repeat_byte(0xff);
|
||||
let fork_id = None;
|
||||
let req_protocol_names = ReqProtocolNames::new(genesis_hash, fork_id);
|
||||
let peerset_protocol_names = PeerSetProtocolNames::new(genesis_hash, fork_id);
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
let (network, network_handle, discovery, network_notification_sinks) =
|
||||
new_test_network(peerset_protocol_names.clone());
|
||||
|
||||
let (context, virtual_overseer) =
|
||||
pezkuwi_node_subsystem_test_helpers::make_subsystem_context(pool);
|
||||
|
||||
let bridge_out = NetworkBridgeTx::new(
|
||||
network,
|
||||
discovery,
|
||||
Metrics(None),
|
||||
req_protocol_names,
|
||||
peerset_protocol_names,
|
||||
network_notification_sinks,
|
||||
);
|
||||
|
||||
let network_bridge_out_fut = run_network_out(bridge_out, context)
|
||||
.map_err(|e| panic!("bridge-out subsystem execution failed {:?}", e))
|
||||
.map(|_| ());
|
||||
|
||||
let test_fut = test(TestHarness { network_handle, virtual_overseer });
|
||||
|
||||
futures::pin_mut!(test_fut);
|
||||
futures::pin_mut!(network_bridge_out_fut);
|
||||
|
||||
let _ = executor::block_on(future::join(
|
||||
async move {
|
||||
let mut virtual_overseer = test_fut.await;
|
||||
virtual_overseer.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await;
|
||||
},
|
||||
network_bridge_out_fut,
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_messages_to_peers() {
|
||||
test_harness(|test_harness| async move {
|
||||
let TestHarness { mut network_handle, mut virtual_overseer } = test_harness;
|
||||
|
||||
let peer = PeerId::random();
|
||||
|
||||
network_handle
|
||||
.connect_peer(peer, PeerSet::Validation, ObservedRole::Full)
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.expect("Timeout does not occur");
|
||||
|
||||
// the outgoing side does not consume network messages
|
||||
// so the single item sink has to be free explicitly
|
||||
|
||||
network_handle
|
||||
.connect_peer(peer, PeerSet::Collation, ObservedRole::Full)
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.expect("Timeout does not occur");
|
||||
|
||||
// send a validation protocol message.
|
||||
|
||||
{
|
||||
let approval_distribution_message =
|
||||
protocol_v3::ApprovalDistributionMessage::Approvals(Vec::new());
|
||||
|
||||
let message_v1 = protocol_v3::ValidationProtocol::ApprovalDistribution(
|
||||
approval_distribution_message.clone(),
|
||||
);
|
||||
|
||||
virtual_overseer
|
||||
.send(FromOrchestra::Communication {
|
||||
msg: NetworkBridgeTxMessage::SendValidationMessage(
|
||||
vec![peer],
|
||||
ValidationProtocols::V3(message_v1.clone()),
|
||||
),
|
||||
})
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.expect("Timeout does not occur");
|
||||
|
||||
assert_eq!(
|
||||
network_handle
|
||||
.next_network_action()
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.expect("Timeout does not occur"),
|
||||
NetworkAction::WriteNotification(
|
||||
peer,
|
||||
PeerSet::Validation,
|
||||
WireMessage::ProtocolMessage(message_v1).encode(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// send a collation protocol message.
|
||||
|
||||
{
|
||||
let collator_protocol_message = protocol_v1::CollatorProtocolMessage::Declare(
|
||||
Sr25519Keyring::Alice.public().into(),
|
||||
0_u32.into(),
|
||||
dummy_collator_signature(),
|
||||
);
|
||||
|
||||
let message_v1 =
|
||||
protocol_v1::CollationProtocol::CollatorProtocol(collator_protocol_message.clone());
|
||||
|
||||
virtual_overseer
|
||||
.send(FromOrchestra::Communication {
|
||||
msg: NetworkBridgeTxMessage::SendCollationMessage(
|
||||
vec![peer],
|
||||
CollationProtocols::V1(message_v1.clone()),
|
||||
),
|
||||
})
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
network_handle
|
||||
.next_network_action()
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.expect("Timeout does not occur"),
|
||||
NetworkAction::WriteNotification(
|
||||
peer,
|
||||
PeerSet::Collation,
|
||||
WireMessage::ProtocolMessage(message_v1).encode(),
|
||||
)
|
||||
);
|
||||
}
|
||||
virtual_overseer
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! A validator discovery service for the Network Bridge.
|
||||
|
||||
use crate::Network;
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use futures::channel::oneshot;
|
||||
|
||||
use sc_network::multiaddr::{self, Multiaddr};
|
||||
|
||||
pub use pezkuwi_node_network_protocol::authority_discovery::AuthorityDiscovery;
|
||||
use pezkuwi_node_network_protocol::{
|
||||
peer_set::{PeerSet, PeerSetProtocolNames, PerPeerSet},
|
||||
PeerId,
|
||||
};
|
||||
use pezkuwi_primitives::AuthorityDiscoveryId;
|
||||
|
||||
const LOG_TARGET: &str = "teyrchain::validator-discovery";
|
||||
|
||||
pub(super) struct Service<N, AD> {
|
||||
state: PerPeerSet<StatePerPeerSet>,
|
||||
peerset_protocol_names: PeerSetProtocolNames,
|
||||
// PhantomData used to make the struct generic instead of having generic methods
|
||||
_phantom: PhantomData<(N, AD)>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct StatePerPeerSet {
|
||||
previously_requested: HashSet<PeerId>,
|
||||
}
|
||||
|
||||
impl<N: Network, AD: AuthorityDiscovery> Service<N, AD> {
|
||||
pub fn new(peerset_protocol_names: PeerSetProtocolNames) -> Self {
|
||||
Self { state: Default::default(), peerset_protocol_names, _phantom: PhantomData }
|
||||
}
|
||||
|
||||
/// Connect to already resolved addresses.
|
||||
pub async fn on_resolved_request(
|
||||
&mut self,
|
||||
newly_requested: HashSet<Multiaddr>,
|
||||
peer_set: PeerSet,
|
||||
mut network_service: N,
|
||||
) -> N {
|
||||
let state = &mut self.state[peer_set];
|
||||
let new_peer_ids: HashSet<PeerId> = extract_peer_ids(newly_requested.iter().cloned());
|
||||
let num_peers = new_peer_ids.len();
|
||||
|
||||
let peers_to_remove: Vec<PeerId> =
|
||||
state.previously_requested.difference(&new_peer_ids).cloned().collect();
|
||||
let removed = peers_to_remove.len();
|
||||
state.previously_requested = new_peer_ids;
|
||||
|
||||
gum::debug!(
|
||||
target: LOG_TARGET,
|
||||
?peer_set,
|
||||
?num_peers,
|
||||
?removed,
|
||||
"New ConnectToValidators resolved request",
|
||||
);
|
||||
// ask the network to connect to these nodes and not disconnect
|
||||
// from them until removed from the set
|
||||
//
|
||||
// for peer-set management, the main protocol name should be used regardless of
|
||||
// the negotiated version.
|
||||
if let Err(e) = network_service
|
||||
.set_reserved_peers(
|
||||
self.peerset_protocol_names.get_main_name(peer_set),
|
||||
newly_requested,
|
||||
)
|
||||
.await
|
||||
{
|
||||
gum::warn!(target: LOG_TARGET, err = ?e, "AuthorityDiscoveryService returned an invalid multiaddress");
|
||||
}
|
||||
|
||||
network_service
|
||||
}
|
||||
|
||||
/// Connect to already resolved addresses.
|
||||
pub async fn on_add_to_resolved_request(
|
||||
&mut self,
|
||||
newly_requested: HashSet<Multiaddr>,
|
||||
peer_set: PeerSet,
|
||||
mut network_service: N,
|
||||
) -> N {
|
||||
let state = &mut self.state[peer_set];
|
||||
let new_peer_ids: HashSet<PeerId> = extract_peer_ids(newly_requested.iter().cloned());
|
||||
let num_peers = new_peer_ids.len();
|
||||
|
||||
state.previously_requested.extend(new_peer_ids);
|
||||
|
||||
gum::debug!(
|
||||
target: LOG_TARGET,
|
||||
?peer_set,
|
||||
?num_peers,
|
||||
"New add to resolved validators request",
|
||||
);
|
||||
|
||||
// ask the network to connect to these nodes and not disconnect
|
||||
// from them until they are removed from the set.
|
||||
//
|
||||
// for peer-set management, the main protocol name should be used regardless of
|
||||
// the negotiated version.
|
||||
if let Err(e) = network_service
|
||||
.add_peers_to_reserved_set(
|
||||
self.peerset_protocol_names.get_main_name(peer_set),
|
||||
newly_requested,
|
||||
)
|
||||
.await
|
||||
{
|
||||
gum::warn!(target: LOG_TARGET, err = ?e, "AuthorityDiscoveryService returned an invalid multiaddress");
|
||||
}
|
||||
|
||||
network_service
|
||||
}
|
||||
|
||||
/// On a new connection request, a peer set update will be issued.
|
||||
/// It will ask the network to connect to the validators and not disconnect
|
||||
/// from them at least until the next request is issued for the same peer set.
|
||||
///
|
||||
/// This method will also disconnect from previously connected validators not in the
|
||||
/// `validator_ids` set. it takes `network_service` and `authority_discovery_service` by value
|
||||
/// and returns them as a workaround for the Future: Send requirement imposed by async function
|
||||
/// implementation.
|
||||
pub async fn on_request(
|
||||
&mut self,
|
||||
validator_ids: Vec<AuthorityDiscoveryId>,
|
||||
peer_set: PeerSet,
|
||||
failed: oneshot::Sender<usize>,
|
||||
network_service: N,
|
||||
mut authority_discovery_service: AD,
|
||||
) -> (N, AD) {
|
||||
// collect multiaddress of validators
|
||||
let mut failed_to_resolve: usize = 0;
|
||||
let mut newly_requested = HashSet::new();
|
||||
let requested = validator_ids.len();
|
||||
for authority in validator_ids.into_iter() {
|
||||
let result = authority_discovery_service
|
||||
.get_addresses_by_authority_id(authority.clone())
|
||||
.await;
|
||||
if let Some(addresses) = result {
|
||||
newly_requested.extend(addresses);
|
||||
} else {
|
||||
failed_to_resolve += 1;
|
||||
gum::debug!(
|
||||
target: LOG_TARGET,
|
||||
"Authority Discovery couldn't resolve {:?}",
|
||||
authority
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
gum::debug!(
|
||||
target: LOG_TARGET,
|
||||
?peer_set,
|
||||
?requested,
|
||||
?failed_to_resolve,
|
||||
"New ConnectToValidators request",
|
||||
);
|
||||
|
||||
let r = self.on_resolved_request(newly_requested, peer_set, network_service).await;
|
||||
|
||||
let _ = failed.send(failed_to_resolve);
|
||||
|
||||
(r, authority_discovery_service)
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_peer_ids(multiaddr: impl Iterator<Item = Multiaddr>) -> HashSet<PeerId> {
|
||||
multiaddr
|
||||
.filter_map(|mut addr| match addr.pop() {
|
||||
Some(multiaddr::Protocol::P2p(key)) => PeerId::from_multihash(key).ok(),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::network::Network;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use pezkuwi_node_network_protocol::{
|
||||
request_response::{outgoing::Requests, ReqProtocolNames},
|
||||
PeerId,
|
||||
};
|
||||
use pezkuwi_primitives::Hash;
|
||||
use sc_network::{IfDisconnected, ProtocolName, ReputationChange};
|
||||
use sp_keyring::Sr25519Keyring;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
fn new_service() -> Service<TestNetwork, TestAuthorityDiscovery> {
|
||||
let genesis_hash = Hash::repeat_byte(0xff);
|
||||
let fork_id = None;
|
||||
let protocol_names = PeerSetProtocolNames::new(genesis_hash, fork_id);
|
||||
|
||||
Service::new(protocol_names)
|
||||
}
|
||||
|
||||
fn new_network() -> (TestNetwork, TestAuthorityDiscovery) {
|
||||
(TestNetwork::default(), TestAuthorityDiscovery::new())
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct TestNetwork {
|
||||
peers_set: HashSet<PeerId>,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
struct TestAuthorityDiscovery {
|
||||
by_authority_id: HashMap<AuthorityDiscoveryId, HashSet<Multiaddr>>,
|
||||
by_peer_id: HashMap<PeerId, HashSet<AuthorityDiscoveryId>>,
|
||||
}
|
||||
|
||||
impl TestAuthorityDiscovery {
|
||||
fn new() -> Self {
|
||||
let peer_ids = known_peer_ids();
|
||||
let authorities = known_authorities();
|
||||
let multiaddr = known_multiaddr().into_iter().zip(peer_ids.iter().cloned()).map(
|
||||
|(mut addr, peer_id)| {
|
||||
addr.push(multiaddr::Protocol::P2p(peer_id.into()));
|
||||
HashSet::from([addr])
|
||||
},
|
||||
);
|
||||
Self {
|
||||
by_authority_id: authorities.iter().cloned().zip(multiaddr).collect(),
|
||||
by_peer_id: peer_ids
|
||||
.into_iter()
|
||||
.zip(authorities.into_iter().map(|a| HashSet::from([a])))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Network for TestNetwork {
|
||||
async fn set_reserved_peers(
|
||||
&mut self,
|
||||
_protocol: ProtocolName,
|
||||
multiaddresses: HashSet<Multiaddr>,
|
||||
) -> Result<(), String> {
|
||||
self.peers_set = extract_peer_ids(multiaddresses.into_iter());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_peers_to_reserved_set(
|
||||
&mut self,
|
||||
_protocol: ProtocolName,
|
||||
multiaddresses: HashSet<Multiaddr>,
|
||||
) -> Result<(), String> {
|
||||
self.peers_set.extend(extract_peer_ids(multiaddresses.into_iter()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_from_peers_set(
|
||||
&mut self,
|
||||
_protocol: ProtocolName,
|
||||
peers: Vec<PeerId>,
|
||||
) -> Result<(), String> {
|
||||
self.peers_set.retain(|elem| !peers.contains(elem));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn start_request<AD: AuthorityDiscovery>(
|
||||
&self,
|
||||
_: &mut AD,
|
||||
_: Requests,
|
||||
_: &ReqProtocolNames,
|
||||
_: IfDisconnected,
|
||||
) {
|
||||
}
|
||||
|
||||
fn report_peer(&self, _: PeerId, _: ReputationChange) {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn disconnect_peer(&self, _: PeerId, _: ProtocolName) {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn peer_role(
|
||||
&self,
|
||||
_peer_id: PeerId,
|
||||
_handshake: Vec<u8>,
|
||||
) -> Option<sc_network::ObservedRole> {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl AuthorityDiscovery for TestAuthorityDiscovery {
|
||||
async fn get_addresses_by_authority_id(
|
||||
&mut self,
|
||||
authority: AuthorityDiscoveryId,
|
||||
) -> Option<HashSet<Multiaddr>> {
|
||||
self.by_authority_id.get(&authority).cloned()
|
||||
}
|
||||
|
||||
async fn get_authority_ids_by_peer_id(
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
) -> Option<HashSet<AuthorityDiscoveryId>> {
|
||||
self.by_peer_id.get(&peer_id).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
fn known_authorities() -> Vec<AuthorityDiscoveryId> {
|
||||
[Sr25519Keyring::Alice, Sr25519Keyring::Bob, Sr25519Keyring::Charlie]
|
||||
.iter()
|
||||
.map(|k| k.public().into())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn known_peer_ids() -> Vec<PeerId> {
|
||||
(0..3).map(|_| PeerId::random()).collect()
|
||||
}
|
||||
|
||||
fn known_multiaddr() -> Vec<Multiaddr> {
|
||||
vec![
|
||||
"/ip4/127.0.0.1/tcp/1234".parse().unwrap(),
|
||||
"/ip4/127.0.0.1/tcp/1235".parse().unwrap(),
|
||||
"/ip4/127.0.0.1/tcp/1236".parse().unwrap(),
|
||||
]
|
||||
}
|
||||
// Test cleanup works.
|
||||
#[test]
|
||||
fn old_multiaddrs_are_removed_on_new_request() {
|
||||
let mut service = new_service();
|
||||
|
||||
let (ns, ads) = new_network();
|
||||
|
||||
let authority_ids: Vec<_> =
|
||||
ads.by_peer_id.values().flat_map(|v| v.iter()).cloned().collect();
|
||||
|
||||
futures::executor::block_on(async move {
|
||||
let (failed, _) = oneshot::channel();
|
||||
let (ns, ads) = service
|
||||
.on_request(vec![authority_ids[0].clone()], PeerSet::Validation, failed, ns, ads)
|
||||
.await;
|
||||
|
||||
let (failed, _) = oneshot::channel();
|
||||
let (_, ads) = service
|
||||
.on_request(vec![authority_ids[1].clone()], PeerSet::Validation, failed, ns, ads)
|
||||
.await;
|
||||
|
||||
let state = &service.state[PeerSet::Validation];
|
||||
assert_eq!(state.previously_requested.len(), 1);
|
||||
let peer_1 = extract_peer_ids(
|
||||
ads.by_authority_id.get(&authority_ids[1]).unwrap().clone().into_iter(),
|
||||
)
|
||||
.iter()
|
||||
.cloned()
|
||||
.next()
|
||||
.unwrap();
|
||||
assert!(state.previously_requested.contains(&peer_1));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_resolution_is_reported_properly() {
|
||||
let mut service = new_service();
|
||||
|
||||
let (ns, ads) = new_network();
|
||||
|
||||
let authority_ids: Vec<_> =
|
||||
ads.by_peer_id.values().flat_map(|v| v.iter()).cloned().collect();
|
||||
|
||||
futures::executor::block_on(async move {
|
||||
let (failed, failed_rx) = oneshot::channel();
|
||||
let unknown = Sr25519Keyring::Ferdie.public().into();
|
||||
let (_, ads) = service
|
||||
.on_request(
|
||||
vec![authority_ids[0].clone(), unknown],
|
||||
PeerSet::Validation,
|
||||
failed,
|
||||
ns,
|
||||
ads,
|
||||
)
|
||||
.await;
|
||||
|
||||
let state = &service.state[PeerSet::Validation];
|
||||
assert_eq!(state.previously_requested.len(), 1);
|
||||
let peer_0 = extract_peer_ids(
|
||||
ads.by_authority_id.get(&authority_ids[0]).unwrap().clone().into_iter(),
|
||||
)
|
||||
.iter()
|
||||
.cloned()
|
||||
.next()
|
||||
.unwrap();
|
||||
assert!(state.previously_requested.contains(&peer_0));
|
||||
|
||||
let failed = failed_rx.await.unwrap();
|
||||
assert_eq!(failed, 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user