Network sync refactoring (part 6) (#11940)

* Extract `NetworkKVProvider` trait in `sc-authority-discovery` and remove unnecessary dependency

* Extract `NetworkSyncForkRequest` trait in `sc-finality-grandpa`

* Relax requirements on `SyncOracle` trait, remove extra native methods from `NetworkService` that are already provided by trait impls

* Move `NetworkSigner` trait from `sc-authority-discovery` into `sc-network-common` and de-duplicate methods on `NetworkService`

* Move `NetworkKVProvider` trait from `sc-authority-discovery` into `sc-network-common` and de-duplicate methods on `NetworkService`

* Minimize `sc-authority-discovery` dependency on `sc-network`

* Move `NetworkSyncForkRequest` trait from `sc-finality-grandpa` to `sc-network-common` and de-duplicate methods in `NetworkService`

* Extract `NetworkStatusProvider` trait and de-duplicate methods on `NetworkService`

* Extract `NetworkPeers` trait and de-duplicate methods on `NetworkService`

* Extract `NetworkEventStream` trait and de-duplicate methods on `NetworkService`

* Move more methods from `NetworkService` into `NetworkPeers` trait

* Move `NetworkStateInfo` trait into `sc-network-common`

* Extract `NetworkNotification` trait and de-duplicate methods on `NetworkService`

* Extract `NetworkRequest` trait and de-duplicate methods on `NetworkService`

* Remove `NetworkService::local_peer_id()`, it is already provided by `NetworkStateInfo` impl

* Extract `NetworkTransaction` trait and de-duplicate methods on `NetworkService`

* Extract `NetworkBlock` trait and de-duplicate methods on `NetworkService`

* Remove dependencies on `NetworkService` from most of the methods of `sc-service`

* Address simple review comments
This commit is contained in:
Nazar Mokrynskyi
2022-08-09 21:28:32 +03:00
committed by GitHub
parent 9c56e79c43
commit a685582bfd
49 changed files with 1889 additions and 1029 deletions
@@ -17,7 +17,9 @@ targets = ["x86_64-unknown-linux-gnu"]
prost-build = "0.10"
[dependencies]
async-trait = "0.1.50"
bitflags = "1.3.2"
bytes = "1"
codec = { package = "parity-scale-codec", version = "3.0.0", features = [
"derive",
] }
@@ -29,3 +31,4 @@ sc-peerset = { version = "4.0.0-dev", path = "../../peerset" }
sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" }
sp-finality-grandpa = { version = "4.0.0-dev", path = "../../../primitives/finality-grandpa" }
sp-runtime = { version = "6.0.0", path = "../../../primitives/runtime" }
thiserror = "1.0"
@@ -20,5 +20,7 @@
pub mod config;
pub mod message;
pub mod protocol;
pub mod request_responses;
pub mod service;
pub mod sync;
@@ -0,0 +1,19 @@
// This file is part of Substrate.
// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program 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.
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
pub mod event;
@@ -67,14 +67,14 @@ pub enum Event {
remote: PeerId,
/// The concerned protocol. Each protocol uses a different substream.
/// This is always equal to the value of
/// [`crate::config::NonDefaultSetConfig::notifications_protocol`] of one of the
/// `sc_network::config::NonDefaultSetConfig::notifications_protocol` of one of the
/// configured sets.
protocol: Cow<'static, str>,
/// If the negotiation didn't use the main name of the protocol (the one in
/// `notifications_protocol`), then this field contains which name has actually been
/// used.
/// Always contains a value equal to the value in
/// [`crate::config::NonDefaultSetConfig::fallback_names`].
/// `sc_network::config::NonDefaultSetConfig::fallback_names`.
negotiated_fallback: Option<Cow<'static, str>>,
/// Role of the remote.
role: ObservedRole,
@@ -19,7 +19,7 @@
//! Collection of generic data structures for request-response protocols.
use futures::channel::{mpsc, oneshot};
use libp2p::PeerId;
use libp2p::{request_response::OutboundFailure, PeerId};
use sc_peerset::ReputationChange;
use std::{borrow::Cow, time::Duration};
@@ -115,3 +115,40 @@ pub struct OutgoingResponse {
/// > written to the buffer managed by the operating system.
pub sent_feedback: Option<oneshot::Sender<()>>,
}
/// When sending a request, what to do on a disconnected recipient.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum IfDisconnected {
/// Try to connect to the peer.
TryConnect,
/// Just fail if the destination is not yet connected.
ImmediateError,
}
/// Convenience functions for `IfDisconnected`.
impl IfDisconnected {
/// Shall we connect to a disconnected peer?
pub fn should_connect(self) -> bool {
match self {
Self::TryConnect => true,
Self::ImmediateError => false,
}
}
}
/// Error in a request.
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum RequestFailure {
#[error("We are not currently connected to the requested peer.")]
NotConnected,
#[error("Given protocol hasn't been registered.")]
UnknownProtocol,
#[error("Remote has closed the substream before answering, thereby signaling that it considers the request as valid, but refused to answer it.")]
Refused,
#[error("The remote replied, but the local node is no longer interested in the response.")]
Obsolete,
/// Problem on the network.
#[error("Problem on the network: {0}")]
Network(OutboundFailure),
}
@@ -0,0 +1,660 @@
// This file is part of Substrate.
//
// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
//
// This program 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.
//
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
//
// If you read this, you are very thorough, congratulations.
use crate::{
protocol::event::Event,
request_responses::{IfDisconnected, RequestFailure},
sync::{warp::WarpSyncProgress, StateDownloadProgress, SyncState},
};
use futures::{channel::oneshot, Stream};
pub use libp2p::{identity::error::SigningError, kad::record::Key as KademliaKey};
use libp2p::{Multiaddr, PeerId};
use sc_peerset::ReputationChange;
pub use signature::Signature;
use sp_runtime::traits::{Block as BlockT, NumberFor};
use std::{borrow::Cow, collections::HashSet, future::Future, pin::Pin, sync::Arc};
mod signature;
/// Signer with network identity
pub trait NetworkSigner {
/// Signs the message with the `KeyPair` that defines the local [`PeerId`].
fn sign_with_local_identity(&self, msg: impl AsRef<[u8]>) -> Result<Signature, SigningError>;
}
impl<T> NetworkSigner for Arc<T>
where
T: ?Sized,
T: NetworkSigner,
{
fn sign_with_local_identity(&self, msg: impl AsRef<[u8]>) -> Result<Signature, SigningError> {
T::sign_with_local_identity(self, msg)
}
}
/// Provides access to the networking DHT.
pub trait NetworkDHTProvider {
/// Start getting a value from the DHT.
fn get_value(&self, key: &KademliaKey);
/// Start putting a value in the DHT.
fn put_value(&self, key: KademliaKey, value: Vec<u8>);
}
impl<T> NetworkDHTProvider for Arc<T>
where
T: ?Sized,
T: NetworkDHTProvider,
{
fn get_value(&self, key: &KademliaKey) {
T::get_value(self, key)
}
fn put_value(&self, key: KademliaKey, value: Vec<u8>) {
T::put_value(self, key, value)
}
}
/// Provides an ability to set a fork sync request for a particular block.
pub trait NetworkSyncForkRequest<BlockHash, BlockNumber> {
/// Notifies the sync service to try and sync the given block from the given
/// peers.
///
/// If the given vector of peers is empty then the underlying implementation
/// should make a best effort to fetch the block from any peers it is
/// connected to (NOTE: this assumption will change in the future #3629).
fn set_sync_fork_request(&self, peers: Vec<PeerId>, hash: BlockHash, number: BlockNumber);
}
impl<T, BlockHash, BlockNumber> NetworkSyncForkRequest<BlockHash, BlockNumber> for Arc<T>
where
T: ?Sized,
T: NetworkSyncForkRequest<BlockHash, BlockNumber>,
{
fn set_sync_fork_request(&self, peers: Vec<PeerId>, hash: BlockHash, number: BlockNumber) {
T::set_sync_fork_request(self, peers, hash, number)
}
}
/// Overview status of the network.
#[derive(Clone)]
pub struct NetworkStatus<B: BlockT> {
/// Current global sync state.
pub sync_state: SyncState,
/// Target sync block number.
pub best_seen_block: Option<NumberFor<B>>,
/// Number of peers participating in syncing.
pub num_sync_peers: u32,
/// Total number of connected peers
pub num_connected_peers: usize,
/// Total number of active peers.
pub num_active_peers: usize,
/// The total number of bytes received.
pub total_bytes_inbound: u64,
/// The total number of bytes sent.
pub total_bytes_outbound: u64,
/// State sync in progress.
pub state_sync: Option<StateDownloadProgress>,
/// Warp sync in progress.
pub warp_sync: Option<WarpSyncProgress<B>>,
}
/// Provides high-level status information about network.
#[async_trait::async_trait]
pub trait NetworkStatusProvider<Block: BlockT> {
/// High-level network status information.
///
/// Returns an error if the `NetworkWorker` is no longer running.
async fn status(&self) -> Result<NetworkStatus<Block>, ()>;
}
// Manual implementation to avoid extra boxing here
impl<T, Block: BlockT> NetworkStatusProvider<Block> for Arc<T>
where
T: ?Sized,
T: NetworkStatusProvider<Block>,
{
fn status<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<NetworkStatus<Block>, ()>> + Send + 'async_trait>>
where
'life0: 'async_trait,
Self: 'async_trait,
{
T::status(self)
}
}
/// Provides low-level API for manipulating network peers.
pub trait NetworkPeers {
/// Set authorized peers.
///
/// Need a better solution to manage authorized peers, but now just use reserved peers for
/// prototyping.
fn set_authorized_peers(&self, peers: HashSet<PeerId>);
/// Set authorized_only flag.
///
/// Need a better solution to decide authorized_only, but now just use reserved_only flag for
/// prototyping.
fn set_authorized_only(&self, reserved_only: bool);
/// Adds an address known to a node.
fn add_known_address(&self, peer_id: PeerId, addr: Multiaddr);
/// Report a given peer as either beneficial (+) or costly (-) according to the
/// given scalar.
fn report_peer(&self, who: PeerId, cost_benefit: ReputationChange);
/// Disconnect from a node as soon as possible.
///
/// This triggers the same effects as if the connection had closed itself spontaneously.
///
/// See also [`NetworkPeers::remove_from_peers_set`], which has the same effect but also
/// prevents the local node from re-establishing an outgoing substream to this peer until it
/// is added again.
fn disconnect_peer(&self, who: PeerId, protocol: Cow<'static, str>);
/// Connect to unreserved peers and allow unreserved peers to connect for syncing purposes.
fn accept_unreserved_peers(&self);
/// Disconnect from unreserved peers and deny new unreserved peers to connect for syncing
/// purposes.
fn deny_unreserved_peers(&self);
/// Adds a `PeerId` and its address as reserved. The string should encode the address
/// and peer ID of the remote node.
///
/// Returns an `Err` if the given string is not a valid multiaddress
/// or contains an invalid peer ID (which includes the local peer ID).
fn add_reserved_peer(&self, peer: String) -> Result<(), String>;
/// Removes a `PeerId` from the list of reserved peers.
fn remove_reserved_peer(&self, peer_id: PeerId);
/// Sets the reserved set of a protocol to the given set of peers.
///
/// Each `Multiaddr` must end with a `/p2p/` component containing the `PeerId`. It can also
/// consist of only `/p2p/<peerid>`.
///
/// The node will start establishing/accepting connections and substreams to/from peers in this
/// set, if it doesn't have any substream open with them yet.
///
/// Note however, if a call to this function results in less peers on the reserved set, they
/// will not necessarily get disconnected (depending on available free slots in the peer set).
/// If you want to also disconnect those removed peers, you will have to call
/// `remove_from_peers_set` on those in addition to updating the reserved set. You can omit
/// this step if the peer set is in reserved only mode.
///
/// Returns an `Err` if one of the given addresses is invalid or contains an
/// invalid peer ID (which includes the local peer ID).
fn set_reserved_peers(
&self,
protocol: Cow<'static, str>,
peers: HashSet<Multiaddr>,
) -> Result<(), String>;
/// Add peers to a peer set.
///
/// Each `Multiaddr` must end with a `/p2p/` component containing the `PeerId`. It can also
/// consist of only `/p2p/<peerid>`.
///
/// Returns an `Err` if one of the given addresses is invalid or contains an
/// invalid peer ID (which includes the local peer ID).
fn add_peers_to_reserved_set(
&self,
protocol: Cow<'static, str>,
peers: HashSet<Multiaddr>,
) -> Result<(), String>;
/// Remove peers from a peer set.
fn remove_peers_from_reserved_set(&self, protocol: Cow<'static, str>, peers: Vec<PeerId>);
/// Add a peer to a set of peers.
///
/// If the set has slots available, it will try to open a substream with this peer.
///
/// Each `Multiaddr` must end with a `/p2p/` component containing the `PeerId`. It can also
/// consist of only `/p2p/<peerid>`.
///
/// Returns an `Err` if one of the given addresses is invalid or contains an
/// invalid peer ID (which includes the local peer ID).
fn add_to_peers_set(
&self,
protocol: Cow<'static, str>,
peers: HashSet<Multiaddr>,
) -> Result<(), String>;
/// Remove peers from a peer set.
///
/// If we currently have an open substream with this peer, it will soon be closed.
fn remove_from_peers_set(&self, protocol: Cow<'static, str>, peers: Vec<PeerId>);
/// Returns the number of peers in the sync peer set we're connected to.
fn sync_num_connected(&self) -> usize;
}
// Manual implementation to avoid extra boxing here
impl<T> NetworkPeers for Arc<T>
where
T: ?Sized,
T: NetworkPeers,
{
fn set_authorized_peers(&self, peers: HashSet<PeerId>) {
T::set_authorized_peers(self, peers)
}
fn set_authorized_only(&self, reserved_only: bool) {
T::set_authorized_only(self, reserved_only)
}
fn add_known_address(&self, peer_id: PeerId, addr: Multiaddr) {
T::add_known_address(self, peer_id, addr)
}
fn report_peer(&self, who: PeerId, cost_benefit: ReputationChange) {
T::report_peer(self, who, cost_benefit)
}
fn disconnect_peer(&self, who: PeerId, protocol: Cow<'static, str>) {
T::disconnect_peer(self, who, protocol)
}
fn accept_unreserved_peers(&self) {
T::accept_unreserved_peers(self)
}
fn deny_unreserved_peers(&self) {
T::deny_unreserved_peers(self)
}
fn add_reserved_peer(&self, peer: String) -> Result<(), String> {
T::add_reserved_peer(self, peer)
}
fn remove_reserved_peer(&self, peer_id: PeerId) {
T::remove_reserved_peer(self, peer_id)
}
fn set_reserved_peers(
&self,
protocol: Cow<'static, str>,
peers: HashSet<Multiaddr>,
) -> Result<(), String> {
T::set_reserved_peers(self, protocol, peers)
}
fn add_peers_to_reserved_set(
&self,
protocol: Cow<'static, str>,
peers: HashSet<Multiaddr>,
) -> Result<(), String> {
T::add_peers_to_reserved_set(self, protocol, peers)
}
fn remove_peers_from_reserved_set(&self, protocol: Cow<'static, str>, peers: Vec<PeerId>) {
T::remove_peers_from_reserved_set(self, protocol, peers)
}
fn add_to_peers_set(
&self,
protocol: Cow<'static, str>,
peers: HashSet<Multiaddr>,
) -> Result<(), String> {
T::add_to_peers_set(self, protocol, peers)
}
fn remove_from_peers_set(&self, protocol: Cow<'static, str>, peers: Vec<PeerId>) {
T::remove_from_peers_set(self, protocol, peers)
}
fn sync_num_connected(&self) -> usize {
T::sync_num_connected(self)
}
}
/// Provides access to network-level event stream.
pub trait NetworkEventStream {
/// Returns a stream containing the events that happen on the network.
///
/// If this method is called multiple times, the events are duplicated.
///
/// The stream never ends (unless the `NetworkWorker` gets shut down).
///
/// The name passed is used to identify the channel in the Prometheus metrics. Note that the
/// parameter is a `&'static str`, and not a `String`, in order to avoid accidentally having
/// an unbounded set of Prometheus metrics, which would be quite bad in terms of memory
fn event_stream(&self, name: &'static str) -> Pin<Box<dyn Stream<Item = Event> + Send>>;
}
impl<T> NetworkEventStream for Arc<T>
where
T: ?Sized,
T: NetworkEventStream,
{
fn event_stream(&self, name: &'static str) -> Pin<Box<dyn Stream<Item = Event> + Send>> {
T::event_stream(self, name)
}
}
/// Trait for providing information about the local network state
pub trait NetworkStateInfo {
/// Returns the local external addresses.
fn external_addresses(&self) -> Vec<Multiaddr>;
/// Returns the local Peer ID.
fn local_peer_id(&self) -> PeerId;
}
impl<T> NetworkStateInfo for Arc<T>
where
T: ?Sized,
T: NetworkStateInfo,
{
fn external_addresses(&self) -> Vec<Multiaddr> {
T::external_addresses(self)
}
fn local_peer_id(&self) -> PeerId {
T::local_peer_id(self)
}
}
/// Reserved slot in the notifications buffer, ready to accept data.
pub trait NotificationSenderReady {
/// Consumes this slots reservation and actually queues the notification.
///
/// NOTE: Traits can't consume itself, but calling this method second time will return an error.
fn send(&mut self, notification: Vec<u8>) -> Result<(), NotificationSenderError>;
}
/// A `NotificationSender` allows for sending notifications to a peer with a chosen protocol.
#[async_trait::async_trait]
pub trait NotificationSender {
/// Returns a future that resolves when the `NotificationSender` is ready to send a
/// notification.
async fn ready(&self)
-> Result<Box<dyn NotificationSenderReady + '_>, NotificationSenderError>;
}
/// Error returned by [`NetworkNotification::notification_sender`].
#[derive(Debug, thiserror::Error)]
pub enum NotificationSenderError {
/// The notification receiver has been closed, usually because the underlying connection
/// closed.
///
/// Some of the notifications most recently sent may not have been received. However,
/// the peer may still be connected and a new `NotificationSender` for the same
/// protocol obtained from [`NetworkNotification::notification_sender`].
#[error("The notification receiver has been closed")]
Closed,
/// Protocol name hasn't been registered.
#[error("Protocol name hasn't been registered")]
BadProtocol,
}
/// Provides ability to send network notifications.
pub trait NetworkNotification {
/// Appends a notification to the buffer of pending outgoing notifications with the given peer.
/// Has no effect if the notifications channel with this protocol name is not open.
///
/// If the buffer of pending outgoing notifications with that peer is full, the notification
/// is silently dropped and the connection to the remote will start being shut down. This
/// happens if you call this method at a higher rate than the rate at which the peer processes
/// these notifications, or if the available network bandwidth is too low.
///
/// For this reason, this method is considered soft-deprecated. You are encouraged to use
/// [`NetworkNotification::notification_sender`] instead.
///
/// > **Note**: The reason why this is a no-op in the situation where we have no channel is
/// > that we don't guarantee message delivery anyway. Networking issues can cause
/// > connections to drop at any time, and higher-level logic shouldn't differentiate
/// > between the remote voluntarily closing a substream or a network error
/// > preventing the message from being delivered.
///
/// The protocol must have been registered with
/// `crate::config::NetworkConfiguration::notifications_protocols`.
fn write_notification(&self, target: PeerId, protocol: Cow<'static, str>, message: Vec<u8>);
/// Obtains a [`NotificationSender`] for a connected peer, if it exists.
///
/// A `NotificationSender` is scoped to a particular connection to the peer that holds
/// a receiver. With a `NotificationSender` at hand, sending a notification is done in two
/// steps:
///
/// 1. [`NotificationSender::ready`] is used to wait for the sender to become ready
/// for another notification, yielding a [`NotificationSenderReady`] token.
/// 2. [`NotificationSenderReady::send`] enqueues the notification for sending. This operation
/// can only fail if the underlying notification substream or connection has suddenly closed.
///
/// An error is returned by [`NotificationSenderReady::send`] if there exists no open
/// notifications substream with that combination of peer and protocol, or if the remote
/// has asked to close the notifications substream. If that happens, it is guaranteed that an
/// [`Event::NotificationStreamClosed`] has been generated on the stream returned by
/// [`NetworkEventStream::event_stream`].
///
/// If the remote requests to close the notifications substream, all notifications successfully
/// enqueued using [`NotificationSenderReady::send`] will finish being sent out before the
/// substream actually gets closed, but attempting to enqueue more notifications will now
/// return an error. It is however possible for the entire connection to be abruptly closed,
/// in which case enqueued notifications will be lost.
///
/// The protocol must have been registered with
/// `crate::config::NetworkConfiguration::notifications_protocols`.
///
/// # Usage
///
/// This method returns a struct that allows waiting until there is space available in the
/// buffer of messages towards the given peer. If the peer processes notifications at a slower
/// rate than we send them, this buffer will quickly fill up.
///
/// As such, you should never do something like this:
///
/// ```ignore
/// // Do NOT do this
/// for peer in peers {
/// if let Ok(n) = network.notification_sender(peer, ...) {
/// if let Ok(s) = n.ready().await {
/// let _ = s.send(...);
/// }
/// }
/// }
/// ```
///
/// Doing so would slow down all peers to the rate of the slowest one. A malicious or
/// malfunctioning peer could intentionally process notifications at a very slow rate.
///
/// Instead, you are encouraged to maintain your own buffer of notifications on top of the one
/// maintained by `sc-network`, and use `notification_sender` to progressively send out
/// elements from your buffer. If this additional buffer is full (which will happen at some
/// point if the peer is too slow to process notifications), appropriate measures can be taken,
/// such as removing non-critical notifications from the buffer or disconnecting the peer
/// using [`NetworkPeers::disconnect_peer`].
///
///
/// Notifications Per-peer buffer
/// broadcast +-------> of notifications +--> `notification_sender` +--> Internet
/// ^ (not covered by
/// | sc-network)
/// +
/// Notifications should be dropped
/// if buffer is full
///
///
/// See also the `sc-network-gossip` crate for a higher-level way to send notifications.
fn notification_sender(
&self,
target: PeerId,
protocol: Cow<'static, str>,
) -> Result<Box<dyn NotificationSender>, NotificationSenderError>;
}
impl<T> NetworkNotification for Arc<T>
where
T: ?Sized,
T: NetworkNotification,
{
fn write_notification(&self, target: PeerId, protocol: Cow<'static, str>, message: Vec<u8>) {
T::write_notification(self, target, protocol, message)
}
fn notification_sender(
&self,
target: PeerId,
protocol: Cow<'static, str>,
) -> Result<Box<dyn NotificationSender>, NotificationSenderError> {
T::notification_sender(self, target, protocol)
}
}
/// Provides ability to send network requests.
#[async_trait::async_trait]
pub trait NetworkRequest {
/// Sends a single targeted request to a specific peer. On success, returns the response of
/// the peer.
///
/// Request-response protocols are a way to complement notifications protocols, but
/// notifications should remain the default ways of communicating information. For example, a
/// peer can announce something through a notification, after which the recipient can obtain
/// more information by performing a request.
/// As such, call this function with `IfDisconnected::ImmediateError` for `connect`. This way
/// you will get an error immediately for disconnected peers, instead of waiting for a
/// potentially very long connection attempt, which would suggest that something is wrong
/// anyway, as you are supposed to be connected because of the notification protocol.
///
/// No limit or throttling of concurrent outbound requests per peer and protocol are enforced.
/// Such restrictions, if desired, need to be enforced at the call site(s).
///
/// The protocol must have been registered through
/// `NetworkConfiguration::request_response_protocols`.
async fn request(
&self,
target: PeerId,
protocol: Cow<'static, str>,
request: Vec<u8>,
connect: IfDisconnected,
) -> Result<Vec<u8>, RequestFailure>;
/// Variation of `request` which starts a request whose response is delivered on a provided
/// channel.
///
/// Instead of blocking and waiting for a reply, this function returns immediately, sending
/// responses via the passed in sender. This alternative API exists to make it easier to
/// integrate with message passing APIs.
///
/// Keep in mind that the connected receiver might receive a `Canceled` event in case of a
/// closing connection. This is expected behaviour. With `request` you would get a
/// `RequestFailure::Network(OutboundFailure::ConnectionClosed)` in that case.
fn start_request(
&self,
target: PeerId,
protocol: Cow<'static, str>,
request: Vec<u8>,
tx: oneshot::Sender<Result<Vec<u8>, RequestFailure>>,
connect: IfDisconnected,
);
}
// Manual implementation to avoid extra boxing here
impl<T> NetworkRequest for Arc<T>
where
T: ?Sized,
T: NetworkRequest,
{
fn request<'life0, 'async_trait>(
&'life0 self,
target: PeerId,
protocol: Cow<'static, str>,
request: Vec<u8>,
connect: IfDisconnected,
) -> Pin<Box<dyn Future<Output = Result<Vec<u8>, RequestFailure>> + Send + 'async_trait>>
where
'life0: 'async_trait,
Self: 'async_trait,
{
T::request(self, target, protocol, request, connect)
}
fn start_request(
&self,
target: PeerId,
protocol: Cow<'static, str>,
request: Vec<u8>,
tx: oneshot::Sender<Result<Vec<u8>, RequestFailure>>,
connect: IfDisconnected,
) {
T::start_request(self, target, protocol, request, tx, connect)
}
}
/// Provides ability to propagate transactions over the network.
pub trait NetworkTransaction<H> {
/// You may call this when new transactions are imported by the transaction pool.
///
/// All transactions will be fetched from the `TransactionPool` that was passed at
/// initialization as part of the configuration and propagated to peers.
fn trigger_repropagate(&self);
/// You must call when new transaction is imported by the transaction pool.
///
/// This transaction will be fetched from the `TransactionPool` that was passed at
/// initialization as part of the configuration and propagated to peers.
fn propagate_transaction(&self, hash: H);
}
impl<T, H> NetworkTransaction<H> for Arc<T>
where
T: ?Sized,
T: NetworkTransaction<H>,
{
fn trigger_repropagate(&self) {
T::trigger_repropagate(self)
}
fn propagate_transaction(&self, hash: H) {
T::propagate_transaction(self, hash)
}
}
/// Provides ability to announce blocks to the network.
pub trait NetworkBlock<BlockHash, BlockNumber> {
/// Make sure an important block is propagated to peers.
///
/// In chain-based consensus, we often need to make sure non-best forks are
/// at least temporarily synced. This function forces such an announcement.
fn announce_block(&self, hash: BlockHash, data: Option<Vec<u8>>);
/// Inform the network service about new best imported block.
fn new_best_block_imported(&self, hash: BlockHash, number: BlockNumber);
}
impl<T, BlockHash, BlockNumber> NetworkBlock<BlockHash, BlockNumber> for Arc<T>
where
T: ?Sized,
T: NetworkBlock<BlockHash, BlockNumber>,
{
fn announce_block(&self, hash: BlockHash, data: Option<Vec<u8>>) {
T::announce_block(self, hash, data)
}
fn new_best_block_imported(&self, hash: BlockHash, number: BlockNumber) {
T::new_best_block_imported(self, hash, number)
}
}
@@ -18,7 +18,10 @@
//
// If you read this, you are very thorough, congratulations.
use super::*;
use libp2p::{
identity::{error::SigningError, Keypair, PublicKey},
PeerId,
};
/// A result of signing a message with a network identity. Since `PeerId` is potentially a hash of a
/// `PublicKey`, you need to reveal the `PublicKey` next to the signature, so the verifier can check
+7 -5
View File
@@ -21,7 +21,7 @@ use crate::{
discovery::{DiscoveryBehaviour, DiscoveryConfig, DiscoveryOut},
peer_info,
protocol::{message::Roles, CustomMessageOutcome, NotificationsSink, Protocol},
request_responses, DhtEvent, ObservedRole,
request_responses,
};
use bytes::Bytes;
@@ -41,7 +41,11 @@ use log::debug;
use sc_client_api::{BlockBackend, ProofProvider};
use sc_consensus::import_queue::{IncomingBlock, Origin};
use sc_network_common::{config::ProtocolId, request_responses::ProtocolConfig};
use sc_network_common::{
config::ProtocolId,
protocol::event::{DhtEvent, ObservedRole},
request_responses::{IfDisconnected, ProtocolConfig, RequestFailure},
};
use sc_peerset::PeersetHandle;
use sp_blockchain::{HeaderBackend, HeaderMetadata};
use sp_consensus::BlockOrigin;
@@ -57,9 +61,7 @@ use std::{
time::Duration,
};
pub use crate::request_responses::{
IfDisconnected, InboundFailure, OutboundFailure, RequestFailure, RequestId, ResponseFailure,
};
pub use crate::request_responses::{InboundFailure, OutboundFailure, RequestId, ResponseFailure};
/// General behaviour of the network. Combines all protocols together.
#[derive(NetworkBehaviour)]
+15 -43
View File
@@ -262,22 +262,26 @@ pub mod transactions;
#[doc(inline)]
pub use libp2p::{multiaddr, Multiaddr, PeerId};
pub use protocol::{
event::{DhtEvent, Event, ObservedRole},
PeerInfo,
};
pub use sc_network_common::sync::{
warp::{WarpSyncPhase, WarpSyncProgress},
StateDownloadProgress, SyncState,
pub use protocol::PeerInfo;
pub use sc_network_common::{
protocol::event::{DhtEvent, Event, ObservedRole},
request_responses::{IfDisconnected, RequestFailure},
service::{
KademliaKey, NetworkBlock, NetworkDHTProvider, NetworkRequest, NetworkSigner,
NetworkStateInfo, NetworkStatus, NetworkStatusProvider, NetworkSyncForkRequest,
NetworkTransaction, Signature, SigningError,
},
sync::{
warp::{WarpSyncPhase, WarpSyncProgress},
StateDownloadProgress, SyncState,
},
};
pub use service::{
DecodingError, IfDisconnected, KademliaKey, Keypair, NetworkService, NetworkWorker,
NotificationSender, NotificationSenderReady, OutboundFailure, PublicKey, RequestFailure,
Signature, SigningError,
DecodingError, Keypair, NetworkService, NetworkWorker, NotificationSender,
NotificationSenderReady, OutboundFailure, PublicKey,
};
pub use sc_peerset::ReputationChange;
use sp_runtime::traits::{Block as BlockT, NumberFor};
/// The maximum allowed number of established connections per peer.
///
@@ -296,35 +300,3 @@ pub trait ExHashT: std::hash::Hash + Eq + std::fmt::Debug + Clone + Send + Sync
impl<T> ExHashT for T where T: std::hash::Hash + Eq + std::fmt::Debug + Clone + Send + Sync + 'static
{}
/// Trait for providing information about the local network state
pub trait NetworkStateInfo {
/// Returns the local external addresses.
fn external_addresses(&self) -> Vec<Multiaddr>;
/// Returns the local Peer ID.
fn local_peer_id(&self) -> PeerId;
}
/// Overview status of the network.
#[derive(Clone)]
pub struct NetworkStatus<B: BlockT> {
/// Current global sync state.
pub sync_state: SyncState,
/// Target sync block number.
pub best_seen_block: Option<NumberFor<B>>,
/// Number of peers participating in syncing.
pub num_sync_peers: u32,
/// Total number of connected peers
pub num_connected_peers: usize,
/// Total number of active peers.
pub num_active_peers: usize,
/// The total number of bytes received.
pub total_bytes_inbound: u64,
/// The total number of bytes sent.
pub total_bytes_outbound: u64,
/// State sync in progress.
pub state_sync: Option<StateDownloadProgress>,
/// Warp sync in progress.
pub warp_sync: Option<WarpSyncProgress<B>>,
}
+1 -2
View File
@@ -18,7 +18,6 @@
use crate::{
config, error,
request_responses::RequestFailure,
utils::{interval, LruHashSet},
};
@@ -45,6 +44,7 @@ use sc_client_api::{BlockBackend, HeaderBackend, ProofProvider};
use sc_consensus::import_queue::{BlockImportError, BlockImportStatus, IncomingBlock, Origin};
use sc_network_common::{
config::ProtocolId,
request_responses::RequestFailure,
sync::{
message::{
BlockAnnounce, BlockAttributes, BlockData, BlockRequest, BlockResponse, BlockState,
@@ -76,7 +76,6 @@ use std::{
mod notifications;
pub mod event;
pub mod message;
pub use notifications::{NotificationsSink, NotifsHandlerError, Ready};
@@ -50,7 +50,9 @@ use libp2p::{
NetworkBehaviourAction, PollParameters,
},
};
use sc_network_common::request_responses::{IncomingRequest, OutgoingResponse, ProtocolConfig};
use sc_network_common::request_responses::{
IfDisconnected, IncomingRequest, OutgoingResponse, ProtocolConfig, RequestFailure,
};
use std::{
borrow::Cow,
collections::{hash_map::Entry, HashMap},
@@ -118,26 +120,6 @@ impl From<(Cow<'static, str>, RequestId)> for ProtocolRequestId {
}
}
/// When sending a request, what to do on a disconnected recipient.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum IfDisconnected {
/// Try to connect to the peer.
TryConnect,
/// Just fail if the destination is not yet connected.
ImmediateError,
}
/// Convenience functions for `IfDisconnected`.
impl IfDisconnected {
/// Shall we connect to a disconnected peer?
pub fn should_connect(self) -> bool {
match self {
Self::TryConnect => true,
Self::ImmediateError => false,
}
}
}
/// Implementation of `NetworkBehaviour` that provides support for request-response protocols.
pub struct RequestResponsesBehaviour {
/// The multiple sub-protocols, by name.
@@ -787,23 +769,6 @@ pub enum RegisterError {
DuplicateProtocol(Cow<'static, str>),
}
/// Error in a request.
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum RequestFailure {
#[error("We are not currently connected to the requested peer.")]
NotConnected,
#[error("Given protocol hasn't been registered.")]
UnknownProtocol,
#[error("Remote has closed the substream before answering, thereby signaling that it considers the request as valid, but refused to answer it.")]
Refused,
#[error("The remote replied, but the local node is no longer interested in the response.")]
Obsolete,
/// Problem on the network.
#[error("Problem on the network: {0}")]
Network(OutboundFailure),
}
/// Error when processing a request sent by a remote.
#[derive(Debug, thiserror::Error)]
pub enum ResponseFailure {
File diff suppressed because it is too large Load Diff
@@ -31,11 +31,10 @@
//! - Send events by calling [`OutChannels::send`]. Events are cloned for each sender in the
//! collection.
use crate::Event;
use futures::{channel::mpsc, prelude::*, ready, stream::FusedStream};
use parking_lot::Mutex;
use prometheus_endpoint::{register, CounterVec, GaugeVec, Opts, PrometheusError, Registry, U64};
use sc_network_common::protocol::event::Event;
use std::{
cell::RefCell,
fmt,
+30 -29
View File
@@ -16,11 +16,15 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::{config, Event, NetworkService, NetworkWorker};
use crate::{config, NetworkService, NetworkWorker};
use futures::prelude::*;
use libp2p::PeerId;
use sc_network_common::config::ProtocolId;
use sc_network_common::{
config::ProtocolId,
protocol::event::Event,
service::{NetworkEventStream, NetworkNotification, NetworkPeers, NetworkStateInfo},
};
use sc_network_light::light_client_requests::handler::LightClientRequestHandler;
use sc_network_sync::{
block_request_handler::BlockRequestHandler, state_request_handler::StateRequestHandler,
@@ -192,7 +196,7 @@ fn build_nodes_one_proto() -> (
set_config: config::SetConfig {
reserved_nodes: vec![config::MultiaddrWithPeerId {
multiaddr: listen_addr,
peer_id: node1.local_peer_id().clone(),
peer_id: node1.local_peer_id(),
}],
..Default::default()
},
@@ -214,18 +218,10 @@ fn notifications_state_consistent() {
// Write some initial notifications that shouldn't get through.
for _ in 0..(rand::random::<u8>() % 5) {
node1.write_notification(
node2.local_peer_id().clone(),
PROTOCOL_NAME,
b"hello world".to_vec(),
);
node1.write_notification(node2.local_peer_id(), PROTOCOL_NAME, b"hello world".to_vec());
}
for _ in 0..(rand::random::<u8>() % 5) {
node2.write_notification(
node1.local_peer_id().clone(),
PROTOCOL_NAME,
b"hello world".to_vec(),
);
node2.write_notification(node1.local_peer_id(), PROTOCOL_NAME, b"hello world".to_vec());
}
async_std::task::block_on(async move {
@@ -249,14 +245,14 @@ fn notifications_state_consistent() {
// test consists in ensuring that notifications get ignored if the stream isn't open.
if rand::random::<u8>() % 5 >= 3 {
node1.write_notification(
node2.local_peer_id().clone(),
node2.local_peer_id(),
PROTOCOL_NAME,
b"hello world".to_vec(),
);
}
if rand::random::<u8>() % 5 >= 3 {
node2.write_notification(
node1.local_peer_id().clone(),
node1.local_peer_id(),
PROTOCOL_NAME,
b"hello world".to_vec(),
);
@@ -264,10 +260,10 @@ fn notifications_state_consistent() {
// Also randomly disconnect the two nodes from time to time.
if rand::random::<u8>() % 20 == 0 {
node1.disconnect_peer(node2.local_peer_id().clone(), PROTOCOL_NAME);
node1.disconnect_peer(node2.local_peer_id(), PROTOCOL_NAME);
}
if rand::random::<u8>() % 20 == 0 {
node2.disconnect_peer(node1.local_peer_id().clone(), PROTOCOL_NAME);
node2.disconnect_peer(node1.local_peer_id(), PROTOCOL_NAME);
}
// Grab next event from either `events_stream1` or `events_stream2`.
@@ -295,7 +291,7 @@ fn notifications_state_consistent() {
something_happened = true;
assert!(!node1_to_node2_open);
node1_to_node2_open = true;
assert_eq!(remote, *node2.local_peer_id());
assert_eq!(remote, node2.local_peer_id());
},
future::Either::Right(Event::NotificationStreamOpened {
remote, protocol, ..
@@ -304,7 +300,7 @@ fn notifications_state_consistent() {
something_happened = true;
assert!(!node2_to_node1_open);
node2_to_node1_open = true;
assert_eq!(remote, *node1.local_peer_id());
assert_eq!(remote, node1.local_peer_id());
},
future::Either::Left(Event::NotificationStreamClosed {
remote, protocol, ..
@@ -312,7 +308,7 @@ fn notifications_state_consistent() {
if protocol == PROTOCOL_NAME {
assert!(node1_to_node2_open);
node1_to_node2_open = false;
assert_eq!(remote, *node2.local_peer_id());
assert_eq!(remote, node2.local_peer_id());
},
future::Either::Right(Event::NotificationStreamClosed {
remote, protocol, ..
@@ -320,14 +316,14 @@ fn notifications_state_consistent() {
if protocol == PROTOCOL_NAME {
assert!(node2_to_node1_open);
node2_to_node1_open = false;
assert_eq!(remote, *node1.local_peer_id());
assert_eq!(remote, node1.local_peer_id());
},
future::Either::Left(Event::NotificationsReceived { remote, .. }) => {
assert!(node1_to_node2_open);
assert_eq!(remote, *node2.local_peer_id());
assert_eq!(remote, node2.local_peer_id());
if rand::random::<u8>() % 5 >= 4 {
node1.write_notification(
node2.local_peer_id().clone(),
node2.local_peer_id(),
PROTOCOL_NAME,
b"hello world".to_vec(),
);
@@ -335,10 +331,10 @@ fn notifications_state_consistent() {
},
future::Either::Right(Event::NotificationsReceived { remote, .. }) => {
assert!(node2_to_node1_open);
assert_eq!(remote, *node1.local_peer_id());
assert_eq!(remote, node1.local_peer_id());
if rand::random::<u8>() % 5 >= 4 {
node2.write_notification(
node1.local_peer_id().clone(),
node1.local_peer_id(),
PROTOCOL_NAME,
b"hello world".to_vec(),
);
@@ -373,7 +369,7 @@ fn lots_of_incoming_peers_works() {
..config::NetworkConfiguration::new_local()
});
let main_node_peer_id = *main_node.local_peer_id();
let main_node_peer_id = main_node.local_peer_id();
// We spawn background tasks and push them in this `Vec`. They will all be waited upon before
// this test ends.
@@ -476,8 +472,13 @@ fn notifications_back_pressure() {
// Sending!
for num in 0..TOTAL_NOTIFS {
let notif = node1.notification_sender(node2_id.clone(), PROTOCOL_NAME).unwrap();
notif.ready().await.unwrap().send(format!("hello #{}", num)).unwrap();
let notif = node1.notification_sender(node2_id, PROTOCOL_NAME).unwrap();
notif
.ready()
.await
.unwrap()
.send(format!("hello #{}", num).into_bytes())
.unwrap();
}
receiver.await;
@@ -514,7 +515,7 @@ fn fallback_name_working() {
set_config: config::SetConfig {
reserved_nodes: vec![config::MultiaddrWithPeerId {
multiaddr: listen_addr,
peer_id: node1.local_peer_id().clone(),
peer_id: node1.local_peer_id(),
}],
..Default::default()
},
+7 -3
View File
@@ -32,7 +32,7 @@ use crate::{
protocol::message,
service::NetworkService,
utils::{interval, LruHashSet},
Event, ExHashT, ObservedRole,
ExHashT,
};
use codec::{Decode, Encode};
@@ -40,7 +40,11 @@ use futures::{channel::mpsc, prelude::*, stream::FuturesUnordered};
use libp2p::{multiaddr, PeerId};
use log::{debug, trace, warn};
use prometheus_endpoint::{register, Counter, PrometheusError, Registry, U64};
use sc_network_common::config::ProtocolId;
use sc_network_common::{
config::ProtocolId,
protocol::event::{Event, ObservedRole},
service::{NetworkEventStream, NetworkNotification, NetworkPeers},
};
use sp_runtime::traits::Block as BlockT;
use std::{
borrow::Cow,
@@ -176,7 +180,7 @@ impl TransactionsHandlerPrototype {
transaction_pool: Arc<dyn TransactionPool<H, B>>,
metrics_registry: Option<&Registry>,
) -> error::Result<(TransactionsHandler<B, H>, TransactionsHandlerController<H>)> {
let event_stream = service.event_stream("transactions-handler").boxed();
let event_stream = service.event_stream("transactions-handler");
let (to_handler, from_controller) = mpsc::unbounded();
let gossip_enabled = Arc::new(AtomicBool::new(false));
+9 -7
View File
@@ -45,7 +45,8 @@ use sc_client_api::{
};
use sc_consensus::{
BasicQueue, BlockCheckParams, BlockImport, BlockImportParams, BoxJustificationImport,
ForkChoiceStrategy, ImportResult, JustificationImport, LongestChain, Verifier,
ForkChoiceStrategy, ImportResult, JustificationImport, JustificationSyncLink, LongestChain,
Verifier,
};
pub use sc_network::config::EmptyTransactionPool;
use sc_network::{
@@ -56,8 +57,9 @@ use sc_network::{
Multiaddr, NetworkService, NetworkWorker,
};
pub use sc_network_common::config::ProtocolId;
use sc_network_common::sync::warp::{
AuthorityList, EncodedProof, SetId, VerificationResult, WarpSyncProvider,
use sc_network_common::{
service::{NetworkBlock, NetworkStateInfo, NetworkSyncForkRequest},
sync::warp::{AuthorityList, EncodedProof, SetId, VerificationResult, WarpSyncProvider},
};
use sc_network_light::light_client_requests::handler::LightClientRequestHandler;
use sc_network_sync::{
@@ -71,7 +73,7 @@ use sp_blockchain::{
};
use sp_consensus::{
block_validation::{BlockAnnounceValidator, DefaultBlockAnnounceValidator},
BlockOrigin, Error as ConsensusError,
BlockOrigin, Error as ConsensusError, SyncOracle,
};
use sp_core::H256;
use sp_runtime::{
@@ -243,7 +245,7 @@ where
{
/// Get this peer ID.
pub fn id(&self) -> PeerId {
*self.network.service().local_peer_id()
self.network.service().local_peer_id()
}
/// Returns true if we're major syncing.
@@ -797,7 +799,7 @@ where
let addrs = connect_to
.iter()
.map(|v| {
let peer_id = *self.peer(*v).network_service().local_peer_id();
let peer_id = self.peer(*v).network_service().local_peer_id();
let multiaddr = self.peer(*v).listen_addr.clone();
MultiaddrWithPeerId { peer_id, multiaddr }
})
@@ -893,7 +895,7 @@ where
self.mut_peers(move |peers| {
for peer in peers.iter_mut() {
peer.network
.add_known_address(*network.service().local_peer_id(), listen_addr.clone());
.add_known_address(network.service().local_peer_id(), listen_addr.clone());
}
let imported_blocks_stream = Box::pin(client.import_notification_stream().fuse());