mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 21:41:12 +00:00
Extract syncing protocol from sc-network (#12828)
* Move import queue out of `sc-network` Add supplementary asynchronous API for the import queue which means it can be run as an independent task and communicated with through the `ImportQueueService`. This commit removes removes block and justification imports from `sc-network` and provides `ChainSync` with a handle to import queue so it can import blocks and justifications. Polling of the import queue is moved complete out of `sc-network` and `sc_consensus::Link` is implemented for `ChainSyncInterfaceHandled` so the import queue can still influence the syncing process. * Move stuff to SyncingEngine * Move `ChainSync` instanation to `SyncingEngine` Some of the tests have to be rewritten * Move peer hashmap to `SyncingEngine` * Let `SyncingEngine` to implement `ChainSyncInterface` * Introduce `SyncStatusProvider` * Move `sync_peer_(connected|disconnected)` to `SyncingEngine` * Implement `SyncEventStream` Remove `SyncConnected`/`SyncDisconnected` events from `NetworkEvenStream` and provide those events through `ChainSyncInterface` instead. Modify BEEFY/GRANDPA/transactions protocol and `NetworkGossip` to take `SyncEventStream` object which they listen to for incoming sync peer events. * Introduce `ChainSyncInterface` This interface provides a set of miscellaneous functions that other subsystems can use to query, for example, the syncing status. * Move event stream polling to `SyncingEngine` Subscribe to `NetworkStreamEvent` and poll the incoming notifications and substream events from `SyncingEngine`. The code needs refactoring. * Make `SyncingEngine` into an asynchronous runner This commits removes the last hard dependency of syncing from `sc-network` meaning the protocol now lives completely outside of `sc-network`, ignoring the hardcoded peerset entry which will be addressed in the future. Code needs a lot of refactoring. * Fix warnings * Code refactoring * Use `SyncingService` for BEEFY * Use `SyncingService` for GRANDPA * Remove call delegation from `NetworkService` * Remove `ChainSyncService` * Remove `ChainSync` service tests They were written for the sole purpose of verifying that `NetworWorker` continues to function while the calls are being dispatched to `ChainSync`. * Refactor code * Refactor code * Update client/finality-grandpa/src/communication/tests.rs Co-authored-by: Anton <anton.kalyaev@gmail.com> * Fix warnings * Apply review comments * Fix docs * Fix test * cargo-fmt * Update client/network/sync/src/engine.rs Co-authored-by: Anton <anton.kalyaev@gmail.com> * Update client/network/sync/src/engine.rs Co-authored-by: Anton <anton.kalyaev@gmail.com> * Add missing docs * Refactor code --------- Co-authored-by: Anton <anton.kalyaev@gmail.com>
This commit is contained in:
@@ -16,6 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"]
|
||||
prost-build = "0.11"
|
||||
|
||||
[dependencies]
|
||||
array-bytes = "4.1"
|
||||
async-trait = "0.1.57"
|
||||
bitflags = "1.3.2"
|
||||
bytes = "1"
|
||||
@@ -30,9 +31,14 @@ prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.
|
||||
smallvec = "1.8.0"
|
||||
sc-consensus = { version = "0.10.0-dev", path = "../../consensus/common" }
|
||||
sc-peerset = { version = "4.0.0-dev", path = "../../peerset" }
|
||||
sc-utils = { version = "4.0.0-dev", path = "../../utils" }
|
||||
serde = { version = "1.0.136", features = ["derive"] }
|
||||
sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" }
|
||||
sp-consensus-grandpa = { version = "4.0.0-dev", path = "../../../primitives/consensus/grandpa" }
|
||||
sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" }
|
||||
sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" }
|
||||
thiserror = "1.0"
|
||||
zeroize = "1.4.3"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.1.0"
|
||||
|
||||
@@ -18,11 +18,33 @@
|
||||
|
||||
//! Configuration of the networking layer.
|
||||
|
||||
use crate::protocol;
|
||||
pub use crate::{
|
||||
protocol::{self, role::Role},
|
||||
request_responses::{
|
||||
IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig,
|
||||
},
|
||||
sync::warp::WarpSyncProvider,
|
||||
ExHashT,
|
||||
};
|
||||
pub use libp2p::{build_multiaddr, core::PublicKey, identity};
|
||||
|
||||
use codec::Encode;
|
||||
use libp2p::{multiaddr, Multiaddr, PeerId};
|
||||
use std::{fmt, str, str::FromStr};
|
||||
use libp2p::{
|
||||
identity::{ed25519, Keypair},
|
||||
multiaddr, Multiaddr, PeerId,
|
||||
};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt, fs,
|
||||
io::{self, Write},
|
||||
iter,
|
||||
net::Ipv4Addr,
|
||||
path::{Path, PathBuf},
|
||||
str,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
/// Protocol name prefix, transmitted on the wire for legacy protocol names.
|
||||
/// I.e., `dot` in `/dot/sync/2`. Should be unique for each chain. Always UTF-8.
|
||||
@@ -331,3 +353,350 @@ impl NonReservedPeerMode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sync operation mode.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum SyncMode {
|
||||
/// Full block download and verification.
|
||||
Full,
|
||||
/// Download blocks and the latest state.
|
||||
Fast {
|
||||
/// Skip state proof download and verification.
|
||||
skip_proofs: bool,
|
||||
/// Download indexed transactions for recent blocks.
|
||||
storage_chain_mode: bool,
|
||||
},
|
||||
/// Warp sync - verify authority set transitions and the latest state.
|
||||
Warp,
|
||||
}
|
||||
|
||||
impl SyncMode {
|
||||
/// Returns if `self` is [`Self::Warp`].
|
||||
pub fn is_warp(&self) -> bool {
|
||||
matches!(self, Self::Warp)
|
||||
}
|
||||
|
||||
/// Returns if `self` is [`Self::Fast`].
|
||||
pub fn is_fast(&self) -> bool {
|
||||
matches!(self, Self::Fast { .. })
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SyncMode {
|
||||
fn default() -> Self {
|
||||
Self::Full
|
||||
}
|
||||
}
|
||||
|
||||
/// Network service configuration.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NetworkConfiguration {
|
||||
/// Directory path to store network-specific configuration. None means nothing will be saved.
|
||||
pub net_config_path: Option<PathBuf>,
|
||||
/// Multiaddresses to listen for incoming connections.
|
||||
pub listen_addresses: Vec<Multiaddr>,
|
||||
/// Multiaddresses to advertise. Detected automatically if empty.
|
||||
pub public_addresses: Vec<Multiaddr>,
|
||||
/// List of initial node addresses
|
||||
pub boot_nodes: Vec<MultiaddrWithPeerId>,
|
||||
/// The node key configuration, which determines the node's network identity keypair.
|
||||
pub node_key: NodeKeyConfig,
|
||||
/// List of request-response protocols that the node supports.
|
||||
pub request_response_protocols: Vec<RequestResponseConfig>,
|
||||
/// Configuration for the default set of nodes used for block syncing and transactions.
|
||||
pub default_peers_set: SetConfig,
|
||||
/// Number of substreams to reserve for full nodes for block syncing and transactions.
|
||||
/// Any other slot will be dedicated to light nodes.
|
||||
///
|
||||
/// This value is implicitly capped to `default_set.out_peers + default_set.in_peers`.
|
||||
pub default_peers_set_num_full: u32,
|
||||
/// Configuration for extra sets of nodes.
|
||||
pub extra_sets: Vec<NonDefaultSetConfig>,
|
||||
/// Client identifier. Sent over the wire for debugging purposes.
|
||||
pub client_version: String,
|
||||
/// Name of the node. Sent over the wire for debugging purposes.
|
||||
pub node_name: String,
|
||||
/// Configuration for the transport layer.
|
||||
pub transport: TransportConfig,
|
||||
/// Maximum number of peers to ask the same blocks in parallel.
|
||||
pub max_parallel_downloads: u32,
|
||||
/// Initial syncing mode.
|
||||
pub sync_mode: SyncMode,
|
||||
|
||||
/// True if Kademlia random discovery should be enabled.
|
||||
///
|
||||
/// If true, the node will automatically randomly walk the DHT in order to find new peers.
|
||||
pub enable_dht_random_walk: bool,
|
||||
|
||||
/// Should we insert non-global addresses into the DHT?
|
||||
pub allow_non_globals_in_dht: bool,
|
||||
|
||||
/// Require iterative Kademlia DHT queries to use disjoint paths for increased resiliency in
|
||||
/// the presence of potentially adversarial nodes.
|
||||
pub kademlia_disjoint_query_paths: bool,
|
||||
/// Enable serving block data over IPFS bitswap.
|
||||
pub ipfs_server: bool,
|
||||
|
||||
/// Size of Yamux receive window of all substreams. `None` for the default (256kiB).
|
||||
/// Any value less than 256kiB is invalid.
|
||||
///
|
||||
/// # Context
|
||||
///
|
||||
/// By design, notifications substreams on top of Yamux connections only allow up to `N` bytes
|
||||
/// to be transferred at a time, where `N` is the Yamux receive window size configurable here.
|
||||
/// This means, in practice, that every `N` bytes must be acknowledged by the receiver before
|
||||
/// the sender can send more data. The maximum bandwidth of each notifications substream is
|
||||
/// therefore `N / round_trip_time`.
|
||||
///
|
||||
/// It is recommended to leave this to `None`, and use a request-response protocol instead if
|
||||
/// a large amount of data must be transferred. The reason why the value is configurable is
|
||||
/// that some Substrate users mis-use notification protocols to send large amounts of data.
|
||||
/// As such, this option isn't designed to stay and will likely get removed in the future.
|
||||
///
|
||||
/// Note that configuring a value here isn't a modification of the Yamux protocol, but rather
|
||||
/// a modification of the way the implementation works. Different nodes with different
|
||||
/// configured values remain compatible with each other.
|
||||
pub yamux_window_size: Option<u32>,
|
||||
}
|
||||
|
||||
impl NetworkConfiguration {
|
||||
/// Create new default configuration
|
||||
pub fn new<SN: Into<String>, SV: Into<String>>(
|
||||
node_name: SN,
|
||||
client_version: SV,
|
||||
node_key: NodeKeyConfig,
|
||||
net_config_path: Option<PathBuf>,
|
||||
) -> Self {
|
||||
let default_peers_set = SetConfig::default();
|
||||
Self {
|
||||
net_config_path,
|
||||
listen_addresses: Vec::new(),
|
||||
public_addresses: Vec::new(),
|
||||
boot_nodes: Vec::new(),
|
||||
node_key,
|
||||
request_response_protocols: Vec::new(),
|
||||
default_peers_set_num_full: default_peers_set.in_peers + default_peers_set.out_peers,
|
||||
default_peers_set,
|
||||
extra_sets: Vec::new(),
|
||||
client_version: client_version.into(),
|
||||
node_name: node_name.into(),
|
||||
transport: TransportConfig::Normal { enable_mdns: false, allow_private_ip: true },
|
||||
max_parallel_downloads: 5,
|
||||
sync_mode: SyncMode::Full,
|
||||
enable_dht_random_walk: true,
|
||||
allow_non_globals_in_dht: false,
|
||||
kademlia_disjoint_query_paths: false,
|
||||
yamux_window_size: None,
|
||||
ipfs_server: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create new default configuration for localhost-only connection with random port (useful for
|
||||
/// testing)
|
||||
pub fn new_local() -> NetworkConfiguration {
|
||||
let mut config =
|
||||
NetworkConfiguration::new("test-node", "test-client", Default::default(), None);
|
||||
|
||||
config.listen_addresses =
|
||||
vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1)))
|
||||
.chain(iter::once(multiaddr::Protocol::Tcp(0)))
|
||||
.collect()];
|
||||
|
||||
config.allow_non_globals_in_dht = true;
|
||||
config
|
||||
}
|
||||
|
||||
/// Create new default configuration for localhost-only connection with random port (useful for
|
||||
/// testing)
|
||||
pub fn new_memory() -> NetworkConfiguration {
|
||||
let mut config =
|
||||
NetworkConfiguration::new("test-node", "test-client", Default::default(), None);
|
||||
|
||||
config.listen_addresses =
|
||||
vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1)))
|
||||
.chain(iter::once(multiaddr::Protocol::Tcp(0)))
|
||||
.collect()];
|
||||
|
||||
config.allow_non_globals_in_dht = true;
|
||||
config
|
||||
}
|
||||
}
|
||||
|
||||
/// The configuration of a node's secret key, describing the type of key
|
||||
/// and how it is obtained. A node's identity keypair is the result of
|
||||
/// the evaluation of the node key configuration.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum NodeKeyConfig {
|
||||
/// A Ed25519 secret key configuration.
|
||||
Ed25519(Secret<ed25519::SecretKey>),
|
||||
}
|
||||
|
||||
impl Default for NodeKeyConfig {
|
||||
fn default() -> NodeKeyConfig {
|
||||
Self::Ed25519(Secret::New)
|
||||
}
|
||||
}
|
||||
|
||||
/// The options for obtaining a Ed25519 secret key.
|
||||
pub type Ed25519Secret = Secret<ed25519::SecretKey>;
|
||||
|
||||
/// The configuration options for obtaining a secret key `K`.
|
||||
#[derive(Clone)]
|
||||
pub enum Secret<K> {
|
||||
/// Use the given secret key `K`.
|
||||
Input(K),
|
||||
/// Read the secret key from a file. If the file does not exist,
|
||||
/// it is created with a newly generated secret key `K`. The format
|
||||
/// of the file is determined by `K`:
|
||||
///
|
||||
/// * `ed25519::SecretKey`: An unencoded 32 bytes Ed25519 secret key.
|
||||
File(PathBuf),
|
||||
/// Always generate a new secret key `K`.
|
||||
New,
|
||||
}
|
||||
|
||||
impl<K> fmt::Debug for Secret<K> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Input(_) => f.debug_tuple("Secret::Input").finish(),
|
||||
Self::File(path) => f.debug_tuple("Secret::File").field(path).finish(),
|
||||
Self::New => f.debug_tuple("Secret::New").finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeKeyConfig {
|
||||
/// Evaluate a `NodeKeyConfig` to obtain an identity `Keypair`:
|
||||
///
|
||||
/// * If the secret is configured as input, the corresponding keypair is returned.
|
||||
///
|
||||
/// * If the secret is configured as a file, it is read from that file, if it exists. Otherwise
|
||||
/// a new secret is generated and stored. In either case, the keypair obtained from the
|
||||
/// secret is returned.
|
||||
///
|
||||
/// * If the secret is configured to be new, it is generated and the corresponding keypair is
|
||||
/// returned.
|
||||
pub fn into_keypair(self) -> io::Result<Keypair> {
|
||||
use NodeKeyConfig::*;
|
||||
match self {
|
||||
Ed25519(Secret::New) => Ok(Keypair::generate_ed25519()),
|
||||
|
||||
Ed25519(Secret::Input(k)) => Ok(Keypair::Ed25519(k.into())),
|
||||
|
||||
Ed25519(Secret::File(f)) => get_secret(
|
||||
f,
|
||||
|mut b| match String::from_utf8(b.to_vec()).ok().and_then(|s| {
|
||||
if s.len() == 64 {
|
||||
array_bytes::hex2bytes(&s).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some(s) => ed25519::SecretKey::from_bytes(s),
|
||||
_ => ed25519::SecretKey::from_bytes(&mut b),
|
||||
},
|
||||
ed25519::SecretKey::generate,
|
||||
|b| b.as_ref().to_vec(),
|
||||
)
|
||||
.map(ed25519::Keypair::from)
|
||||
.map(Keypair::Ed25519),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Load a secret key from a file, if it exists, or generate a
|
||||
/// new secret key and write it to that file. In either case,
|
||||
/// the secret key is returned.
|
||||
fn get_secret<P, F, G, E, W, K>(file: P, parse: F, generate: G, serialize: W) -> io::Result<K>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
F: for<'r> FnOnce(&'r mut [u8]) -> Result<K, E>,
|
||||
G: FnOnce() -> K,
|
||||
E: Error + Send + Sync + 'static,
|
||||
W: Fn(&K) -> Vec<u8>,
|
||||
{
|
||||
std::fs::read(&file)
|
||||
.and_then(|mut sk_bytes| {
|
||||
parse(&mut sk_bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||
})
|
||||
.or_else(|e| {
|
||||
if e.kind() == io::ErrorKind::NotFound {
|
||||
file.as_ref().parent().map_or(Ok(()), fs::create_dir_all)?;
|
||||
let sk = generate();
|
||||
let mut sk_vec = serialize(&sk);
|
||||
write_secret_file(file, &sk_vec)?;
|
||||
sk_vec.zeroize();
|
||||
Ok(sk)
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Write secret bytes to a file.
|
||||
fn write_secret_file<P>(path: P, sk_bytes: &[u8]) -> io::Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut file = open_secret_file(&path)?;
|
||||
file.write_all(sk_bytes)
|
||||
}
|
||||
|
||||
/// Opens a file containing a secret key in write mode.
|
||||
#[cfg(unix)]
|
||||
fn open_secret_file<P>(path: P) -> io::Result<fs::File>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
fs::OpenOptions::new().write(true).create_new(true).mode(0o600).open(path)
|
||||
}
|
||||
|
||||
/// Opens a file containing a secret key in write mode.
|
||||
#[cfg(not(unix))]
|
||||
fn open_secret_file<P>(path: P) -> Result<fs::File, io::Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
fs::OpenOptions::new().write(true).create_new(true).open(path)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tempfile::TempDir;
|
||||
|
||||
fn tempdir_with_prefix(prefix: &str) -> TempDir {
|
||||
tempfile::Builder::new().prefix(prefix).tempdir().unwrap()
|
||||
}
|
||||
|
||||
fn secret_bytes(kp: &Keypair) -> Vec<u8> {
|
||||
let Keypair::Ed25519(p) = kp;
|
||||
p.secret().as_ref().iter().cloned().collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_secret_file() {
|
||||
let tmp = tempdir_with_prefix("x");
|
||||
std::fs::remove_dir(tmp.path()).unwrap(); // should be recreated
|
||||
let file = tmp.path().join("x").to_path_buf();
|
||||
let kp1 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap();
|
||||
let kp2 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap();
|
||||
assert!(file.is_file() && secret_bytes(&kp1) == secret_bytes(&kp2))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_secret_input() {
|
||||
let sk = ed25519::SecretKey::generate();
|
||||
let kp1 = NodeKeyConfig::Ed25519(Secret::Input(sk.clone())).into_keypair().unwrap();
|
||||
let kp2 = NodeKeyConfig::Ed25519(Secret::Input(sk)).into_keypair().unwrap();
|
||||
assert!(secret_bytes(&kp1) == secret_bytes(&kp2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_secret_new() {
|
||||
let kp1 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap();
|
||||
let kp2 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap();
|
||||
assert!(secret_bytes(&kp1) != secret_bytes(&kp2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,18 +48,6 @@ pub enum Event {
|
||||
/// Event generated by a DHT.
|
||||
Dht(DhtEvent),
|
||||
|
||||
/// Now connected to a new peer for syncing purposes.
|
||||
SyncConnected {
|
||||
/// Node we are now syncing from.
|
||||
remote: PeerId,
|
||||
},
|
||||
|
||||
/// Now disconnected from a peer for syncing purposes.
|
||||
SyncDisconnected {
|
||||
/// Node we are no longer syncing from.
|
||||
remote: PeerId,
|
||||
},
|
||||
|
||||
/// Opened a substream with the given node with the given notifications protocol.
|
||||
///
|
||||
/// The protocol is always one of the notification protocols that have been registered.
|
||||
@@ -79,6 +67,8 @@ pub enum Event {
|
||||
negotiated_fallback: Option<ProtocolName>,
|
||||
/// Role of the remote.
|
||||
role: ObservedRole,
|
||||
/// Received handshake.
|
||||
received_handshake: Vec<u8>,
|
||||
},
|
||||
|
||||
/// Closed a substream with the given node. Always matches a corresponding previous
|
||||
|
||||
@@ -22,14 +22,12 @@ use crate::{
|
||||
config::MultiaddrWithPeerId,
|
||||
protocol::{event::Event, ProtocolName},
|
||||
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::{collections::HashSet, future::Future, pin::Pin, sync::Arc};
|
||||
|
||||
mod signature;
|
||||
@@ -96,45 +94,33 @@ where
|
||||
|
||||
/// Overview status of the network.
|
||||
#[derive(Clone)]
|
||||
pub struct NetworkStatus<B: BlockT> {
|
||||
/// Current global sync state.
|
||||
pub sync_state: SyncState<NumberFor<B>>,
|
||||
/// 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 struct NetworkStatus {
|
||||
/// 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> {
|
||||
pub trait NetworkStatusProvider {
|
||||
/// High-level network status information.
|
||||
///
|
||||
/// Returns an error if the `NetworkWorker` is no longer running.
|
||||
async fn status(&self) -> Result<NetworkStatus<Block>, ()>;
|
||||
async fn status(&self) -> Result<NetworkStatus, ()>;
|
||||
}
|
||||
|
||||
// Manual implementation to avoid extra boxing here
|
||||
impl<T, Block: BlockT> NetworkStatusProvider<Block> for Arc<T>
|
||||
impl<T> NetworkStatusProvider for Arc<T>
|
||||
where
|
||||
T: ?Sized,
|
||||
T: NetworkStatusProvider<Block>,
|
||||
T: NetworkStatusProvider,
|
||||
{
|
||||
fn status<'life0, 'async_trait>(
|
||||
&'life0 self,
|
||||
) -> Pin<Box<dyn Future<Output = Result<NetworkStatus<Block>, ()>> + Send + 'async_trait>>
|
||||
) -> Pin<Box<dyn Future<Output = Result<NetworkStatus, ()>> + Send + 'async_trait>>
|
||||
where
|
||||
'life0: 'async_trait,
|
||||
Self: 'async_trait,
|
||||
@@ -511,6 +497,9 @@ pub trait NetworkNotification {
|
||||
target: PeerId,
|
||||
protocol: ProtocolName,
|
||||
) -> Result<Box<dyn NotificationSender>, NotificationSenderError>;
|
||||
|
||||
/// Set handshake for the notification protocol.
|
||||
fn set_notification_handshake(&self, protocol: ProtocolName, handshake: Vec<u8>);
|
||||
}
|
||||
|
||||
impl<T> NetworkNotification for Arc<T>
|
||||
@@ -529,6 +518,10 @@ where
|
||||
) -> Result<Box<dyn NotificationSender>, NotificationSenderError> {
|
||||
T::notification_sender(self, target, protocol)
|
||||
}
|
||||
|
||||
fn set_notification_handshake(&self, protocol: ProtocolName, handshake: Vec<u8>) {
|
||||
T::set_notification_handshake(self, protocol, handshake)
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides ability to send network requests.
|
||||
|
||||
@@ -22,7 +22,11 @@ pub mod message;
|
||||
pub mod metrics;
|
||||
pub mod warp;
|
||||
|
||||
use crate::protocol::role::Roles;
|
||||
use futures::Stream;
|
||||
|
||||
use libp2p::PeerId;
|
||||
|
||||
use message::{BlockAnnounce, BlockData, BlockRequest, BlockResponse};
|
||||
use sc_consensus::{import_queue::RuntimeOrigin, IncomingBlock};
|
||||
use sp_consensus::BlockOrigin;
|
||||
@@ -30,9 +34,10 @@ use sp_runtime::{
|
||||
traits::{Block as BlockT, NumberFor},
|
||||
Justifications,
|
||||
};
|
||||
use std::{any::Any, fmt, fmt::Formatter, task::Poll};
|
||||
use warp::WarpSyncProgress;
|
||||
|
||||
use std::{any::Any, fmt, fmt::Formatter, pin::Pin, sync::Arc, task::Poll};
|
||||
|
||||
/// The sync status of a peer we are trying to sync with
|
||||
#[derive(Debug)]
|
||||
pub struct PeerInfo<Block: BlockT> {
|
||||
@@ -42,6 +47,17 @@ pub struct PeerInfo<Block: BlockT> {
|
||||
pub best_number: NumberFor<Block>,
|
||||
}
|
||||
|
||||
/// Info about a peer's known state (both full and light).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ExtendedPeerInfo<B: BlockT> {
|
||||
/// Roles
|
||||
pub roles: Roles,
|
||||
/// Peer best block hash
|
||||
pub best_hash: B::Hash,
|
||||
/// Peer best block number
|
||||
pub best_number: NumberFor<B>,
|
||||
}
|
||||
|
||||
/// Reported sync state.
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub enum SyncState<BlockNumber> {
|
||||
@@ -251,6 +267,49 @@ impl fmt::Debug for OpaqueBlockResponse {
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides high-level status of syncing.
|
||||
#[async_trait::async_trait]
|
||||
pub trait SyncStatusProvider<Block: BlockT>: Send + Sync {
|
||||
/// Get high-level view of the syncing status.
|
||||
async fn status(&self) -> Result<SyncStatus<Block>, ()>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<T, Block> SyncStatusProvider<Block> for Arc<T>
|
||||
where
|
||||
T: ?Sized,
|
||||
T: SyncStatusProvider<Block>,
|
||||
Block: BlockT,
|
||||
{
|
||||
async fn status(&self) -> Result<SyncStatus<Block>, ()> {
|
||||
T::status(self).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Syncing-related events that other protocols can subscribe to.
|
||||
pub enum SyncEvent {
|
||||
/// Peer that the syncing implementation is tracking connected.
|
||||
PeerConnected(PeerId),
|
||||
|
||||
/// Peer that the syncing implementation was tracking disconnected.
|
||||
PeerDisconnected(PeerId),
|
||||
}
|
||||
|
||||
pub trait SyncEventStream: Send + Sync {
|
||||
/// Subscribe to syncing-related events.
|
||||
fn event_stream(&self, name: &'static str) -> Pin<Box<dyn Stream<Item = SyncEvent> + Send>>;
|
||||
}
|
||||
|
||||
impl<T> SyncEventStream for Arc<T>
|
||||
where
|
||||
T: ?Sized,
|
||||
T: SyncEventStream,
|
||||
{
|
||||
fn event_stream(&self, name: &'static str) -> Pin<Box<dyn Stream<Item = SyncEvent> + Send>> {
|
||||
T::event_stream(self, name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that represents the syncing strategy to download past and future blocks of the chain.
|
||||
pub trait ChainSync<Block: BlockT>: Send {
|
||||
/// Returns the state of the sync of the given peer.
|
||||
|
||||
@@ -41,7 +41,6 @@ use sc_network_common::{
|
||||
request_responses::{IfDisconnected, ProtocolConfig, RequestFailure},
|
||||
};
|
||||
use sc_peerset::{PeersetHandle, ReputationChange};
|
||||
use sp_blockchain::HeaderBackend;
|
||||
use sp_runtime::traits::Block as BlockT;
|
||||
use std::{collections::HashSet, time::Duration};
|
||||
|
||||
@@ -50,13 +49,9 @@ pub use crate::request_responses::{InboundFailure, OutboundFailure, RequestId, R
|
||||
/// General behaviour of the network. Combines all protocols together.
|
||||
#[derive(NetworkBehaviour)]
|
||||
#[behaviour(out_event = "BehaviourOut")]
|
||||
pub struct Behaviour<B, Client>
|
||||
where
|
||||
B: BlockT,
|
||||
Client: HeaderBackend<B> + 'static,
|
||||
{
|
||||
pub struct Behaviour<B: BlockT> {
|
||||
/// All the substrate-specific protocols.
|
||||
substrate: Protocol<B, Client>,
|
||||
substrate: Protocol<B>,
|
||||
/// Periodically pings and identifies the nodes we are connected to, and store information in a
|
||||
/// cache.
|
||||
peer_info: peer_info::PeerInfoBehaviour,
|
||||
@@ -118,6 +113,8 @@ pub enum BehaviourOut {
|
||||
notifications_sink: NotificationsSink,
|
||||
/// Role of the remote.
|
||||
role: ObservedRole,
|
||||
/// Received handshake.
|
||||
received_handshake: Vec<u8>,
|
||||
},
|
||||
|
||||
/// The [`NotificationsSink`] object used to send notifications with the given peer must be
|
||||
@@ -151,12 +148,6 @@ pub enum BehaviourOut {
|
||||
messages: Vec<(ProtocolName, Bytes)>,
|
||||
},
|
||||
|
||||
/// Now connected to a new peer for syncing purposes.
|
||||
SyncConnected(PeerId),
|
||||
|
||||
/// No longer connected to a peer for syncing purposes.
|
||||
SyncDisconnected(PeerId),
|
||||
|
||||
/// We have obtained identity information from a peer, including the addresses it is listening
|
||||
/// on.
|
||||
PeerIdentify {
|
||||
@@ -177,14 +168,10 @@ pub enum BehaviourOut {
|
||||
None,
|
||||
}
|
||||
|
||||
impl<B, Client> Behaviour<B, Client>
|
||||
where
|
||||
B: BlockT,
|
||||
Client: HeaderBackend<B> + 'static,
|
||||
{
|
||||
impl<B: BlockT> Behaviour<B> {
|
||||
/// Builds a new `Behaviour`.
|
||||
pub fn new(
|
||||
substrate: Protocol<B, Client>,
|
||||
substrate: Protocol<B>,
|
||||
user_agent: String,
|
||||
local_public_key: PublicKey,
|
||||
disco_config: DiscoveryConfig,
|
||||
@@ -252,12 +239,12 @@ where
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the user protocol.
|
||||
pub fn user_protocol(&self) -> &Protocol<B, Client> {
|
||||
pub fn user_protocol(&self) -> &Protocol<B> {
|
||||
&self.substrate
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the user protocol.
|
||||
pub fn user_protocol_mut(&mut self) -> &mut Protocol<B, Client> {
|
||||
pub fn user_protocol_mut(&mut self) -> &mut Protocol<B> {
|
||||
&mut self.substrate
|
||||
}
|
||||
|
||||
@@ -295,20 +282,22 @@ fn reported_roles_to_observed_role(roles: Roles) -> ObservedRole {
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BlockT> From<CustomMessageOutcome<B>> for BehaviourOut {
|
||||
fn from(event: CustomMessageOutcome<B>) -> Self {
|
||||
impl From<CustomMessageOutcome> for BehaviourOut {
|
||||
fn from(event: CustomMessageOutcome) -> Self {
|
||||
match event {
|
||||
CustomMessageOutcome::NotificationStreamOpened {
|
||||
remote,
|
||||
protocol,
|
||||
negotiated_fallback,
|
||||
roles,
|
||||
received_handshake,
|
||||
notifications_sink,
|
||||
} => BehaviourOut::NotificationStreamOpened {
|
||||
remote,
|
||||
protocol,
|
||||
negotiated_fallback,
|
||||
role: reported_roles_to_observed_role(roles),
|
||||
received_handshake,
|
||||
notifications_sink,
|
||||
},
|
||||
CustomMessageOutcome::NotificationStreamReplaced {
|
||||
@@ -320,10 +309,6 @@ impl<B: BlockT> From<CustomMessageOutcome<B>> for BehaviourOut {
|
||||
BehaviourOut::NotificationStreamClosed { remote, protocol },
|
||||
CustomMessageOutcome::NotificationsReceived { remote, messages } =>
|
||||
BehaviourOut::NotificationsReceived { remote, messages },
|
||||
CustomMessageOutcome::PeerNewBest(_peer_id, _number) => BehaviourOut::None,
|
||||
CustomMessageOutcome::SyncConnected(peer_id) => BehaviourOut::SyncConnected(peer_id),
|
||||
CustomMessageOutcome::SyncDisconnected(peer_id) =>
|
||||
BehaviourOut::SyncDisconnected(peer_id),
|
||||
CustomMessageOutcome::None => BehaviourOut::None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
//! See the documentation of [`Params`].
|
||||
|
||||
pub use sc_network_common::{
|
||||
config::ProtocolId,
|
||||
config::{NetworkConfiguration, ProtocolId},
|
||||
protocol::role::Role,
|
||||
request_responses::{
|
||||
IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig,
|
||||
@@ -33,35 +33,12 @@ pub use sc_network_common::{
|
||||
|
||||
pub use libp2p::{build_multiaddr, core::PublicKey, identity};
|
||||
|
||||
use crate::ChainSyncInterface;
|
||||
use core::{fmt, iter};
|
||||
use libp2p::{
|
||||
identity::{ed25519, Keypair},
|
||||
multiaddr, Multiaddr,
|
||||
};
|
||||
use prometheus_endpoint::Registry;
|
||||
use sc_network_common::{
|
||||
config::{MultiaddrWithPeerId, NonDefaultSetConfig, SetConfig, TransportConfig},
|
||||
sync::ChainSync,
|
||||
};
|
||||
use sp_runtime::traits::Block as BlockT;
|
||||
use std::{
|
||||
error::Error,
|
||||
fs,
|
||||
future::Future,
|
||||
io::{self, Write},
|
||||
net::Ipv4Addr,
|
||||
path::{Path, PathBuf},
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
};
|
||||
use zeroize::Zeroize;
|
||||
use sc_network_common::config::NonDefaultSetConfig;
|
||||
use std::{future::Future, pin::Pin, sync::Arc};
|
||||
|
||||
/// Network initialization parameters.
|
||||
pub struct Params<B, Client>
|
||||
where
|
||||
B: BlockT + 'static,
|
||||
{
|
||||
pub struct Params<Client> {
|
||||
/// Assigned role for our node (full, light, ...).
|
||||
pub role: Role,
|
||||
|
||||
@@ -81,12 +58,6 @@ where
|
||||
/// name on the wire.
|
||||
pub fork_id: Option<String>,
|
||||
|
||||
/// Instance of chain sync implementation.
|
||||
pub chain_sync: Box<dyn ChainSync<B>>,
|
||||
|
||||
/// Interface that can be used to delegate syncing-related function calls to `ChainSync`
|
||||
pub chain_sync_service: Box<dyn ChainSyncInterface<B>>,
|
||||
|
||||
/// Registry for recording prometheus metrics to.
|
||||
pub metrics_registry: Option<Registry>,
|
||||
|
||||
@@ -96,350 +67,3 @@ where
|
||||
/// Request response protocol configurations
|
||||
pub request_response_protocol_configs: Vec<RequestResponseConfig>,
|
||||
}
|
||||
|
||||
/// Sync operation mode.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum SyncMode {
|
||||
/// Full block download and verification.
|
||||
Full,
|
||||
/// Download blocks and the latest state.
|
||||
Fast {
|
||||
/// Skip state proof download and verification.
|
||||
skip_proofs: bool,
|
||||
/// Download indexed transactions for recent blocks.
|
||||
storage_chain_mode: bool,
|
||||
},
|
||||
/// Warp sync - verify authority set transitions and the latest state.
|
||||
Warp,
|
||||
}
|
||||
|
||||
impl SyncMode {
|
||||
/// Returns if `self` is [`Self::Warp`].
|
||||
pub fn is_warp(&self) -> bool {
|
||||
matches!(self, Self::Warp)
|
||||
}
|
||||
|
||||
/// Returns if `self` is [`Self::Fast`].
|
||||
pub fn is_fast(&self) -> bool {
|
||||
matches!(self, Self::Fast { .. })
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SyncMode {
|
||||
fn default() -> Self {
|
||||
Self::Full
|
||||
}
|
||||
}
|
||||
|
||||
/// Network service configuration.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NetworkConfiguration {
|
||||
/// Directory path to store network-specific configuration. None means nothing will be saved.
|
||||
pub net_config_path: Option<PathBuf>,
|
||||
/// Multiaddresses to listen for incoming connections.
|
||||
pub listen_addresses: Vec<Multiaddr>,
|
||||
/// Multiaddresses to advertise. Detected automatically if empty.
|
||||
pub public_addresses: Vec<Multiaddr>,
|
||||
/// List of initial node addresses
|
||||
pub boot_nodes: Vec<MultiaddrWithPeerId>,
|
||||
/// The node key configuration, which determines the node's network identity keypair.
|
||||
pub node_key: NodeKeyConfig,
|
||||
/// List of request-response protocols that the node supports.
|
||||
pub request_response_protocols: Vec<RequestResponseConfig>,
|
||||
/// Configuration for the default set of nodes used for block syncing and transactions.
|
||||
pub default_peers_set: SetConfig,
|
||||
/// Number of substreams to reserve for full nodes for block syncing and transactions.
|
||||
/// Any other slot will be dedicated to light nodes.
|
||||
///
|
||||
/// This value is implicitly capped to `default_set.out_peers + default_set.in_peers`.
|
||||
pub default_peers_set_num_full: u32,
|
||||
/// Configuration for extra sets of nodes.
|
||||
pub extra_sets: Vec<NonDefaultSetConfig>,
|
||||
/// Client identifier. Sent over the wire for debugging purposes.
|
||||
pub client_version: String,
|
||||
/// Name of the node. Sent over the wire for debugging purposes.
|
||||
pub node_name: String,
|
||||
/// Configuration for the transport layer.
|
||||
pub transport: TransportConfig,
|
||||
/// Maximum number of peers to ask the same blocks in parallel.
|
||||
pub max_parallel_downloads: u32,
|
||||
/// Initial syncing mode.
|
||||
pub sync_mode: SyncMode,
|
||||
|
||||
/// True if Kademlia random discovery should be enabled.
|
||||
///
|
||||
/// If true, the node will automatically randomly walk the DHT in order to find new peers.
|
||||
pub enable_dht_random_walk: bool,
|
||||
|
||||
/// Should we insert non-global addresses into the DHT?
|
||||
pub allow_non_globals_in_dht: bool,
|
||||
|
||||
/// Require iterative Kademlia DHT queries to use disjoint paths for increased resiliency in
|
||||
/// the presence of potentially adversarial nodes.
|
||||
pub kademlia_disjoint_query_paths: bool,
|
||||
/// Enable serving block data over IPFS bitswap.
|
||||
pub ipfs_server: bool,
|
||||
|
||||
/// Size of Yamux receive window of all substreams. `None` for the default (256kiB).
|
||||
/// Any value less than 256kiB is invalid.
|
||||
///
|
||||
/// # Context
|
||||
///
|
||||
/// By design, notifications substreams on top of Yamux connections only allow up to `N` bytes
|
||||
/// to be transferred at a time, where `N` is the Yamux receive window size configurable here.
|
||||
/// This means, in practice, that every `N` bytes must be acknowledged by the receiver before
|
||||
/// the sender can send more data. The maximum bandwidth of each notifications substream is
|
||||
/// therefore `N / round_trip_time`.
|
||||
///
|
||||
/// It is recommended to leave this to `None`, and use a request-response protocol instead if
|
||||
/// a large amount of data must be transferred. The reason why the value is configurable is
|
||||
/// that some Substrate users mis-use notification protocols to send large amounts of data.
|
||||
/// As such, this option isn't designed to stay and will likely get removed in the future.
|
||||
///
|
||||
/// Note that configuring a value here isn't a modification of the Yamux protocol, but rather
|
||||
/// a modification of the way the implementation works. Different nodes with different
|
||||
/// configured values remain compatible with each other.
|
||||
pub yamux_window_size: Option<u32>,
|
||||
}
|
||||
|
||||
impl NetworkConfiguration {
|
||||
/// Create new default configuration
|
||||
pub fn new<SN: Into<String>, SV: Into<String>>(
|
||||
node_name: SN,
|
||||
client_version: SV,
|
||||
node_key: NodeKeyConfig,
|
||||
net_config_path: Option<PathBuf>,
|
||||
) -> Self {
|
||||
let default_peers_set = SetConfig::default();
|
||||
Self {
|
||||
net_config_path,
|
||||
listen_addresses: Vec::new(),
|
||||
public_addresses: Vec::new(),
|
||||
boot_nodes: Vec::new(),
|
||||
node_key,
|
||||
request_response_protocols: Vec::new(),
|
||||
default_peers_set_num_full: default_peers_set.in_peers + default_peers_set.out_peers,
|
||||
default_peers_set,
|
||||
extra_sets: Vec::new(),
|
||||
client_version: client_version.into(),
|
||||
node_name: node_name.into(),
|
||||
transport: TransportConfig::Normal { enable_mdns: false, allow_private_ip: true },
|
||||
max_parallel_downloads: 5,
|
||||
sync_mode: SyncMode::Full,
|
||||
enable_dht_random_walk: true,
|
||||
allow_non_globals_in_dht: false,
|
||||
kademlia_disjoint_query_paths: false,
|
||||
yamux_window_size: None,
|
||||
ipfs_server: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create new default configuration for localhost-only connection with random port (useful for
|
||||
/// testing)
|
||||
pub fn new_local() -> NetworkConfiguration {
|
||||
let mut config =
|
||||
NetworkConfiguration::new("test-node", "test-client", Default::default(), None);
|
||||
|
||||
config.listen_addresses =
|
||||
vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1)))
|
||||
.chain(iter::once(multiaddr::Protocol::Tcp(0)))
|
||||
.collect()];
|
||||
|
||||
config.allow_non_globals_in_dht = true;
|
||||
config
|
||||
}
|
||||
|
||||
/// Create new default configuration for localhost-only connection with random port (useful for
|
||||
/// testing)
|
||||
pub fn new_memory() -> NetworkConfiguration {
|
||||
let mut config =
|
||||
NetworkConfiguration::new("test-node", "test-client", Default::default(), None);
|
||||
|
||||
config.listen_addresses =
|
||||
vec![iter::once(multiaddr::Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1)))
|
||||
.chain(iter::once(multiaddr::Protocol::Tcp(0)))
|
||||
.collect()];
|
||||
|
||||
config.allow_non_globals_in_dht = true;
|
||||
config
|
||||
}
|
||||
}
|
||||
|
||||
/// The configuration of a node's secret key, describing the type of key
|
||||
/// and how it is obtained. A node's identity keypair is the result of
|
||||
/// the evaluation of the node key configuration.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum NodeKeyConfig {
|
||||
/// A Ed25519 secret key configuration.
|
||||
Ed25519(Secret<ed25519::SecretKey>),
|
||||
}
|
||||
|
||||
impl Default for NodeKeyConfig {
|
||||
fn default() -> NodeKeyConfig {
|
||||
Self::Ed25519(Secret::New)
|
||||
}
|
||||
}
|
||||
|
||||
/// The options for obtaining a Ed25519 secret key.
|
||||
pub type Ed25519Secret = Secret<ed25519::SecretKey>;
|
||||
|
||||
/// The configuration options for obtaining a secret key `K`.
|
||||
#[derive(Clone)]
|
||||
pub enum Secret<K> {
|
||||
/// Use the given secret key `K`.
|
||||
Input(K),
|
||||
/// Read the secret key from a file. If the file does not exist,
|
||||
/// it is created with a newly generated secret key `K`. The format
|
||||
/// of the file is determined by `K`:
|
||||
///
|
||||
/// * `ed25519::SecretKey`: An unencoded 32 bytes Ed25519 secret key.
|
||||
File(PathBuf),
|
||||
/// Always generate a new secret key `K`.
|
||||
New,
|
||||
}
|
||||
|
||||
impl<K> fmt::Debug for Secret<K> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Input(_) => f.debug_tuple("Secret::Input").finish(),
|
||||
Self::File(path) => f.debug_tuple("Secret::File").field(path).finish(),
|
||||
Self::New => f.debug_tuple("Secret::New").finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeKeyConfig {
|
||||
/// Evaluate a `NodeKeyConfig` to obtain an identity `Keypair`:
|
||||
///
|
||||
/// * If the secret is configured as input, the corresponding keypair is returned.
|
||||
///
|
||||
/// * If the secret is configured as a file, it is read from that file, if it exists. Otherwise
|
||||
/// a new secret is generated and stored. In either case, the keypair obtained from the
|
||||
/// secret is returned.
|
||||
///
|
||||
/// * If the secret is configured to be new, it is generated and the corresponding keypair is
|
||||
/// returned.
|
||||
pub fn into_keypair(self) -> io::Result<Keypair> {
|
||||
use NodeKeyConfig::*;
|
||||
match self {
|
||||
Ed25519(Secret::New) => Ok(Keypair::generate_ed25519()),
|
||||
|
||||
Ed25519(Secret::Input(k)) => Ok(Keypair::Ed25519(k.into())),
|
||||
|
||||
Ed25519(Secret::File(f)) => get_secret(
|
||||
f,
|
||||
|mut b| match String::from_utf8(b.to_vec()).ok().and_then(|s| {
|
||||
if s.len() == 64 {
|
||||
array_bytes::hex2bytes(&s).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some(s) => ed25519::SecretKey::from_bytes(s),
|
||||
_ => ed25519::SecretKey::from_bytes(&mut b),
|
||||
},
|
||||
ed25519::SecretKey::generate,
|
||||
|b| b.as_ref().to_vec(),
|
||||
)
|
||||
.map(ed25519::Keypair::from)
|
||||
.map(Keypair::Ed25519),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Load a secret key from a file, if it exists, or generate a
|
||||
/// new secret key and write it to that file. In either case,
|
||||
/// the secret key is returned.
|
||||
fn get_secret<P, F, G, E, W, K>(file: P, parse: F, generate: G, serialize: W) -> io::Result<K>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
F: for<'r> FnOnce(&'r mut [u8]) -> Result<K, E>,
|
||||
G: FnOnce() -> K,
|
||||
E: Error + Send + Sync + 'static,
|
||||
W: Fn(&K) -> Vec<u8>,
|
||||
{
|
||||
std::fs::read(&file)
|
||||
.and_then(|mut sk_bytes| {
|
||||
parse(&mut sk_bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||
})
|
||||
.or_else(|e| {
|
||||
if e.kind() == io::ErrorKind::NotFound {
|
||||
file.as_ref().parent().map_or(Ok(()), fs::create_dir_all)?;
|
||||
let sk = generate();
|
||||
let mut sk_vec = serialize(&sk);
|
||||
write_secret_file(file, &sk_vec)?;
|
||||
sk_vec.zeroize();
|
||||
Ok(sk)
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Write secret bytes to a file.
|
||||
fn write_secret_file<P>(path: P, sk_bytes: &[u8]) -> io::Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut file = open_secret_file(&path)?;
|
||||
file.write_all(sk_bytes)
|
||||
}
|
||||
|
||||
/// Opens a file containing a secret key in write mode.
|
||||
#[cfg(unix)]
|
||||
fn open_secret_file<P>(path: P) -> io::Result<fs::File>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
fs::OpenOptions::new().write(true).create_new(true).mode(0o600).open(path)
|
||||
}
|
||||
|
||||
/// Opens a file containing a secret key in write mode.
|
||||
#[cfg(not(unix))]
|
||||
fn open_secret_file<P>(path: P) -> Result<fs::File, io::Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
fs::OpenOptions::new().write(true).create_new(true).open(path)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tempfile::TempDir;
|
||||
|
||||
fn tempdir_with_prefix(prefix: &str) -> TempDir {
|
||||
tempfile::Builder::new().prefix(prefix).tempdir().unwrap()
|
||||
}
|
||||
|
||||
fn secret_bytes(kp: &Keypair) -> Vec<u8> {
|
||||
let Keypair::Ed25519(p) = kp;
|
||||
p.secret().as_ref().iter().cloned().collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_secret_file() {
|
||||
let tmp = tempdir_with_prefix("x");
|
||||
std::fs::remove_dir(tmp.path()).unwrap(); // should be recreated
|
||||
let file = tmp.path().join("x").to_path_buf();
|
||||
let kp1 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap();
|
||||
let kp2 = NodeKeyConfig::Ed25519(Secret::File(file.clone())).into_keypair().unwrap();
|
||||
assert!(file.is_file() && secret_bytes(&kp1) == secret_bytes(&kp2))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_secret_input() {
|
||||
let sk = ed25519::SecretKey::generate();
|
||||
let kp1 = NodeKeyConfig::Ed25519(Secret::Input(sk.clone())).into_keypair().unwrap();
|
||||
let kp2 = NodeKeyConfig::Ed25519(Secret::Input(sk)).into_keypair().unwrap();
|
||||
assert!(secret_bytes(&kp1) == secret_bytes(&kp2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_secret_new() {
|
||||
let kp1 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap();
|
||||
let kp2 = NodeKeyConfig::Ed25519(Secret::New).into_keypair().unwrap();
|
||||
assert!(secret_bytes(&kp1) != secret_bytes(&kp2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,8 +257,6 @@ pub mod network_state;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use libp2p::{multiaddr, Multiaddr, PeerId};
|
||||
pub use protocol::PeerInfo;
|
||||
use sc_consensus::{JustificationSyncLink, Link};
|
||||
pub use sc_network_common::{
|
||||
protocol::{
|
||||
event::{DhtEvent, Event},
|
||||
@@ -273,14 +271,13 @@ pub use sc_network_common::{
|
||||
},
|
||||
sync::{
|
||||
warp::{WarpSyncPhase, WarpSyncProgress},
|
||||
StateDownloadProgress, SyncState,
|
||||
ExtendedPeerInfo, StateDownloadProgress, SyncEventStream, SyncState, SyncStatusProvider,
|
||||
},
|
||||
};
|
||||
pub use service::{
|
||||
DecodingError, Keypair, NetworkService, NetworkWorker, NotificationSender,
|
||||
NotificationSenderReady, OutboundFailure, PublicKey,
|
||||
};
|
||||
use sp_runtime::traits::{Block as BlockT, NumberFor};
|
||||
|
||||
pub use sc_peerset::ReputationChange;
|
||||
|
||||
@@ -295,18 +292,3 @@ const MAX_CONNECTIONS_PER_PEER: usize = 2;
|
||||
|
||||
/// The maximum number of concurrent established connections that were incoming.
|
||||
const MAX_CONNECTIONS_ESTABLISHED_INCOMING: u32 = 10_000;
|
||||
|
||||
/// Abstraction over syncing-related services
|
||||
pub trait ChainSyncInterface<B: BlockT>:
|
||||
NetworkSyncForkRequest<B::Hash, NumberFor<B>> + JustificationSyncLink<B> + Link<B> + Send + Sync
|
||||
{
|
||||
}
|
||||
|
||||
impl<T, B: BlockT> ChainSyncInterface<B> for T where
|
||||
T: NetworkSyncForkRequest<B::Hash, NumberFor<B>>
|
||||
+ JustificationSyncLink<B>
|
||||
+ Link<B>
|
||||
+ Send
|
||||
+ Sync
|
||||
{
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
use crate::config;
|
||||
|
||||
use bytes::Bytes;
|
||||
use codec::{Decode, DecodeAll, Encode};
|
||||
use futures::prelude::*;
|
||||
use codec::{DecodeAll, Encode};
|
||||
use libp2p::{
|
||||
core::connection::ConnectionId,
|
||||
swarm::{
|
||||
@@ -29,32 +28,20 @@ use libp2p::{
|
||||
},
|
||||
Multiaddr, PeerId,
|
||||
};
|
||||
use log::{debug, error, log, trace, warn, Level};
|
||||
use lru::LruCache;
|
||||
use log::{debug, error, warn};
|
||||
use message::{generic::Message as GenericMessage, Message};
|
||||
use notifications::{Notifications, NotificationsOut};
|
||||
use prometheus_endpoint::{register, Gauge, GaugeVec, Opts, PrometheusError, Registry, U64};
|
||||
use sc_client_api::HeaderBackend;
|
||||
use sc_network_common::{
|
||||
config::NonReservedPeerMode,
|
||||
error,
|
||||
protocol::{role::Roles, ProtocolName},
|
||||
sync::{
|
||||
message::{BlockAnnounce, BlockAnnouncesHandshake, BlockData, BlockResponse, BlockState},
|
||||
BadPeer, ChainSync, PollBlockAnnounceValidation, SyncStatus,
|
||||
},
|
||||
utils::{interval, LruHashSet},
|
||||
sync::message::BlockAnnouncesHandshake,
|
||||
};
|
||||
use sp_arithmetic::traits::SaturatedConversion;
|
||||
use sp_runtime::traits::{Block as BlockT, CheckedSub, Header as HeaderT, NumberFor, Zero};
|
||||
use sp_runtime::traits::Block as BlockT;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet, VecDeque},
|
||||
iter,
|
||||
num::NonZeroUsize,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::Poll,
|
||||
time,
|
||||
};
|
||||
|
||||
mod notifications;
|
||||
@@ -63,12 +50,6 @@ pub mod message;
|
||||
|
||||
pub use notifications::{NotificationsSink, NotifsHandlerError, Ready};
|
||||
|
||||
/// Interval at which we perform time based maintenance
|
||||
const TICK_TIMEOUT: time::Duration = time::Duration::from_millis(1100);
|
||||
|
||||
/// Maximum number of known block hashes to keep for a peer.
|
||||
const MAX_KNOWN_BLOCKS: usize = 1024; // ~32kb per peer + LruHashSet overhead
|
||||
|
||||
/// Maximum size used for notifications in the block announce and transaction protocols.
|
||||
// Must be equal to `max(MAX_BLOCK_ANNOUNCE_SIZE, MAX_TRANSACTIONS_SIZE)`.
|
||||
pub(crate) const BLOCK_ANNOUNCES_TRANSACTIONS_SUBSTREAM_SIZE: u64 = 16 * 1024 * 1024;
|
||||
@@ -79,88 +60,16 @@ const HARDCODED_PEERSETS_SYNC: sc_peerset::SetId = sc_peerset::SetId::from(0);
|
||||
/// superior to this value corresponds to a user-defined protocol.
|
||||
const NUM_HARDCODED_PEERSETS: usize = 1;
|
||||
|
||||
/// When light node connects to the full node and the full node is behind light node
|
||||
/// for at least `LIGHT_MAXIMAL_BLOCKS_DIFFERENCE` blocks, we consider it not useful
|
||||
/// and disconnect to free connection slot.
|
||||
const LIGHT_MAXIMAL_BLOCKS_DIFFERENCE: u64 = 8192;
|
||||
|
||||
mod rep {
|
||||
use sc_peerset::ReputationChange as Rep;
|
||||
/// Reputation change when we are a light client and a peer is behind us.
|
||||
pub const PEER_BEHIND_US_LIGHT: Rep = Rep::new(-(1 << 8), "Useless for a light peer");
|
||||
/// We received a message that failed to decode.
|
||||
pub const BAD_MESSAGE: Rep = Rep::new(-(1 << 12), "Bad message");
|
||||
/// Peer has different genesis.
|
||||
pub const GENESIS_MISMATCH: Rep = Rep::new_fatal("Genesis mismatch");
|
||||
/// Peer role does not match (e.g. light peer connecting to another light peer).
|
||||
pub const BAD_ROLE: Rep = Rep::new_fatal("Unsupported role");
|
||||
/// Peer send us a block announcement that failed at validation.
|
||||
pub const BAD_BLOCK_ANNOUNCEMENT: Rep = Rep::new(-(1 << 12), "Bad block announcement");
|
||||
}
|
||||
|
||||
struct Metrics {
|
||||
peers: Gauge<U64>,
|
||||
queued_blocks: Gauge<U64>,
|
||||
fork_targets: Gauge<U64>,
|
||||
justifications: GaugeVec<U64>,
|
||||
}
|
||||
|
||||
impl Metrics {
|
||||
fn register(r: &Registry) -> Result<Self, PrometheusError> {
|
||||
Ok(Self {
|
||||
peers: {
|
||||
let g = Gauge::new("substrate_sync_peers", "Number of peers we sync with")?;
|
||||
register(g, r)?
|
||||
},
|
||||
queued_blocks: {
|
||||
let g =
|
||||
Gauge::new("substrate_sync_queued_blocks", "Number of blocks in import queue")?;
|
||||
register(g, r)?
|
||||
},
|
||||
fork_targets: {
|
||||
let g = Gauge::new("substrate_sync_fork_targets", "Number of fork sync targets")?;
|
||||
register(g, r)?
|
||||
},
|
||||
justifications: {
|
||||
let g = GaugeVec::new(
|
||||
Opts::new(
|
||||
"substrate_sync_extra_justifications",
|
||||
"Number of extra justifications requests",
|
||||
),
|
||||
&["status"],
|
||||
)?;
|
||||
register(g, r)?
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Lock must always be taken in order declared here.
|
||||
pub struct Protocol<B: BlockT, Client> {
|
||||
/// Interval at which we call `tick`.
|
||||
tick_timeout: Pin<Box<dyn Stream<Item = ()> + Send>>,
|
||||
pub struct Protocol<B: BlockT> {
|
||||
/// Pending list of messages to return from `poll` as a priority.
|
||||
pending_messages: VecDeque<CustomMessageOutcome<B>>,
|
||||
/// Assigned roles.
|
||||
roles: Roles,
|
||||
genesis_hash: B::Hash,
|
||||
/// State machine that handles the list of in-progress requests. Only full node peers are
|
||||
/// registered.
|
||||
chain_sync: Box<dyn ChainSync<B>>,
|
||||
// All connected peers. Contains both full and light node peers.
|
||||
peers: HashMap<PeerId, Peer<B>>,
|
||||
chain: Arc<Client>,
|
||||
/// List of nodes for which we perform additional logging because they are important for the
|
||||
/// user.
|
||||
important_peers: HashSet<PeerId>,
|
||||
/// List of nodes that should never occupy peer slots.
|
||||
default_peers_set_no_slot_peers: HashSet<PeerId>,
|
||||
/// Actual list of connected no-slot nodes.
|
||||
default_peers_set_no_slot_connected_peers: HashSet<PeerId>,
|
||||
/// Value that was passed as part of the configuration. Used to cap the number of full nodes.
|
||||
default_peers_set_num_full: usize,
|
||||
/// Number of slots to allocate to light nodes.
|
||||
default_peers_set_num_light: usize,
|
||||
pending_messages: VecDeque<CustomMessageOutcome>,
|
||||
/// Used to report reputation changes.
|
||||
peerset_handle: sc_peerset::PeersetHandle,
|
||||
/// Handles opening the unique substream and sending and receiving raw messages.
|
||||
@@ -174,85 +83,18 @@ pub struct Protocol<B: BlockT, Client> {
|
||||
/// solve this, an entry is added to this map whenever an invalid handshake is received.
|
||||
/// Entries are removed when the corresponding "substream closed" is later received.
|
||||
bad_handshake_substreams: HashSet<(PeerId, sc_peerset::SetId)>,
|
||||
/// Prometheus metrics.
|
||||
metrics: Option<Metrics>,
|
||||
/// The `PeerId`'s of all boot nodes.
|
||||
boot_node_ids: HashSet<PeerId>,
|
||||
/// A cache for the data that was associated to a block announcement.
|
||||
block_announce_data_cache: LruCache<B::Hash, Vec<u8>>,
|
||||
/// Connected peers.
|
||||
peers: HashMap<PeerId, Roles>,
|
||||
_marker: std::marker::PhantomData<B>,
|
||||
}
|
||||
|
||||
/// Peer information
|
||||
#[derive(Debug)]
|
||||
struct Peer<B: BlockT> {
|
||||
info: PeerInfo<B>,
|
||||
/// Holds a set of blocks known to this peer.
|
||||
known_blocks: LruHashSet<B::Hash>,
|
||||
}
|
||||
|
||||
/// Info about a peer's known state.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PeerInfo<B: BlockT> {
|
||||
/// Roles
|
||||
pub roles: Roles,
|
||||
/// Peer best block hash
|
||||
pub best_hash: B::Hash,
|
||||
/// Peer best block number
|
||||
pub best_number: <B::Header as HeaderT>::Number,
|
||||
}
|
||||
|
||||
impl<B, Client> Protocol<B, Client>
|
||||
where
|
||||
B: BlockT,
|
||||
Client: HeaderBackend<B> + 'static,
|
||||
{
|
||||
impl<B: BlockT> Protocol<B> {
|
||||
/// Create a new instance.
|
||||
pub fn new(
|
||||
roles: Roles,
|
||||
chain: Arc<Client>,
|
||||
network_config: &config::NetworkConfiguration,
|
||||
metrics_registry: Option<&Registry>,
|
||||
chain_sync: Box<dyn ChainSync<B>>,
|
||||
block_announces_protocol: sc_network_common::config::NonDefaultSetConfig,
|
||||
) -> error::Result<(Self, sc_peerset::PeersetHandle, Vec<(PeerId, Multiaddr)>)> {
|
||||
let info = chain.info();
|
||||
|
||||
let boot_node_ids = {
|
||||
let mut list = HashSet::new();
|
||||
for node in &network_config.boot_nodes {
|
||||
list.insert(node.peer_id);
|
||||
}
|
||||
list.shrink_to_fit();
|
||||
list
|
||||
};
|
||||
|
||||
let important_peers = {
|
||||
let mut imp_p = HashSet::new();
|
||||
for reserved in &network_config.default_peers_set.reserved_nodes {
|
||||
imp_p.insert(reserved.peer_id);
|
||||
}
|
||||
for reserved in network_config
|
||||
.extra_sets
|
||||
.iter()
|
||||
.flat_map(|s| s.set_config.reserved_nodes.iter())
|
||||
{
|
||||
imp_p.insert(reserved.peer_id);
|
||||
}
|
||||
imp_p.shrink_to_fit();
|
||||
imp_p
|
||||
};
|
||||
|
||||
let default_peers_set_no_slot_peers = {
|
||||
let mut no_slot_p: HashSet<PeerId> = network_config
|
||||
.default_peers_set
|
||||
.reserved_nodes
|
||||
.iter()
|
||||
.map(|reserved| reserved.peer_id)
|
||||
.collect();
|
||||
no_slot_p.shrink_to_fit();
|
||||
no_slot_p
|
||||
};
|
||||
|
||||
let mut known_addresses = Vec::new();
|
||||
|
||||
let (peerset, peerset_handle) = {
|
||||
@@ -326,44 +168,17 @@ where
|
||||
)
|
||||
};
|
||||
|
||||
let cache_capacity = NonZeroUsize::new(
|
||||
(network_config.default_peers_set.in_peers as usize +
|
||||
network_config.default_peers_set.out_peers as usize)
|
||||
.max(1),
|
||||
)
|
||||
.expect("cache capacity is not zero");
|
||||
let block_announce_data_cache = LruCache::new(cache_capacity);
|
||||
|
||||
let protocol = Self {
|
||||
tick_timeout: Box::pin(interval(TICK_TIMEOUT)),
|
||||
pending_messages: VecDeque::new(),
|
||||
roles,
|
||||
peers: HashMap::new(),
|
||||
chain,
|
||||
genesis_hash: info.genesis_hash,
|
||||
chain_sync,
|
||||
important_peers,
|
||||
default_peers_set_no_slot_peers,
|
||||
default_peers_set_no_slot_connected_peers: HashSet::new(),
|
||||
default_peers_set_num_full: network_config.default_peers_set_num_full as usize,
|
||||
default_peers_set_num_light: {
|
||||
let total = network_config.default_peers_set.out_peers +
|
||||
network_config.default_peers_set.in_peers;
|
||||
total.saturating_sub(network_config.default_peers_set_num_full) as usize
|
||||
},
|
||||
peerset_handle: peerset_handle.clone(),
|
||||
behaviour,
|
||||
notification_protocols: iter::once(block_announces_protocol.notifications_protocol)
|
||||
.chain(network_config.extra_sets.iter().map(|s| s.notifications_protocol.clone()))
|
||||
.collect(),
|
||||
bad_handshake_substreams: Default::default(),
|
||||
metrics: if let Some(r) = metrics_registry {
|
||||
Some(Metrics::register(r)?)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
boot_node_ids,
|
||||
block_announce_data_cache,
|
||||
peers: HashMap::new(),
|
||||
// TODO: remove when `BlockAnnouncesHandshake` is moved away from `Protocol`
|
||||
_marker: Default::default(),
|
||||
};
|
||||
|
||||
Ok((protocol, peerset_handle, known_addresses))
|
||||
@@ -384,6 +199,7 @@ where
|
||||
if let Some(position) = self.notification_protocols.iter().position(|p| *p == protocol_name)
|
||||
{
|
||||
self.behaviour.disconnect_peer(peer_id, sc_peerset::SetId::from(position));
|
||||
self.peers.remove(peer_id);
|
||||
} else {
|
||||
warn!(target: "sub-libp2p", "disconnect_peer() with invalid protocol name")
|
||||
}
|
||||
@@ -399,391 +215,23 @@ where
|
||||
self.peers.len()
|
||||
}
|
||||
|
||||
/// Returns the number of peers we're connected to and that are being queried.
|
||||
pub fn num_active_peers(&self) -> usize {
|
||||
self.chain_sync.num_active_peers()
|
||||
}
|
||||
|
||||
/// Current global sync state.
|
||||
pub fn sync_state(&self) -> SyncStatus<B> {
|
||||
self.chain_sync.status()
|
||||
}
|
||||
|
||||
/// Target sync block number.
|
||||
pub fn best_seen_block(&self) -> Option<NumberFor<B>> {
|
||||
self.chain_sync.status().best_seen_block
|
||||
}
|
||||
|
||||
/// Number of peers participating in syncing.
|
||||
pub fn num_sync_peers(&self) -> u32 {
|
||||
self.chain_sync.status().num_peers
|
||||
}
|
||||
|
||||
/// Number of blocks in the import queue.
|
||||
pub fn num_queued_blocks(&self) -> u32 {
|
||||
self.chain_sync.status().queued_blocks
|
||||
}
|
||||
|
||||
/// Number of downloaded blocks.
|
||||
pub fn num_downloaded_blocks(&self) -> usize {
|
||||
self.chain_sync.num_downloaded_blocks()
|
||||
}
|
||||
|
||||
/// Number of active sync requests.
|
||||
pub fn num_sync_requests(&self) -> usize {
|
||||
self.chain_sync.num_sync_requests()
|
||||
}
|
||||
|
||||
/// Inform sync about new best imported block.
|
||||
pub fn new_best_block_imported(&mut self, hash: B::Hash, number: NumberFor<B>) {
|
||||
debug!(target: "sync", "New best block imported {:?}/#{}", hash, number);
|
||||
|
||||
self.chain_sync.update_chain_info(&hash, number);
|
||||
|
||||
self.behaviour.set_notif_protocol_handshake(
|
||||
HARDCODED_PEERSETS_SYNC,
|
||||
BlockAnnouncesHandshake::<B>::build(self.roles, number, hash, self.genesis_hash)
|
||||
.encode(),
|
||||
);
|
||||
}
|
||||
|
||||
fn update_peer_info(&mut self, who: &PeerId) {
|
||||
if let Some(info) = self.chain_sync.peer_info(who) {
|
||||
if let Some(ref mut peer) = self.peers.get_mut(who) {
|
||||
peer.info.best_hash = info.best_hash;
|
||||
peer.info.best_number = info.best_number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns information about all the peers we are connected to after the handshake message.
|
||||
pub fn peers_info(&self) -> impl Iterator<Item = (&PeerId, &PeerInfo<B>)> {
|
||||
self.peers.iter().map(|(id, peer)| (id, &peer.info))
|
||||
}
|
||||
|
||||
/// Called by peer when it is disconnecting.
|
||||
///
|
||||
/// Returns a result if the handshake of this peer was indeed accepted.
|
||||
pub fn on_sync_peer_disconnected(&mut self, peer: PeerId) -> Result<(), ()> {
|
||||
if self.important_peers.contains(&peer) {
|
||||
warn!(target: "sync", "Reserved peer {} disconnected", peer);
|
||||
} else {
|
||||
debug!(target: "sync", "{} disconnected", peer);
|
||||
}
|
||||
|
||||
if let Some(_peer_data) = self.peers.remove(&peer) {
|
||||
self.chain_sync.peer_disconnected(&peer);
|
||||
self.default_peers_set_no_slot_connected_peers.remove(&peer);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Adjusts the reputation of a node.
|
||||
pub fn report_peer(&self, who: PeerId, reputation: sc_peerset::ReputationChange) {
|
||||
self.peerset_handle.report_peer(who, reputation)
|
||||
}
|
||||
|
||||
/// Perform time based maintenance.
|
||||
///
|
||||
/// > **Note**: This method normally doesn't have to be called except for testing purposes.
|
||||
pub fn tick(&mut self) {
|
||||
self.report_metrics()
|
||||
}
|
||||
|
||||
/// Called on the first connection between two peers on the default set, after their exchange
|
||||
/// of handshake.
|
||||
///
|
||||
/// Returns `Ok` if the handshake is accepted and the peer added to the list of peers we sync
|
||||
/// from.
|
||||
fn on_sync_peer_connected(
|
||||
&mut self,
|
||||
who: PeerId,
|
||||
status: BlockAnnouncesHandshake<B>,
|
||||
) -> Result<(), ()> {
|
||||
trace!(target: "sync", "New peer {} {:?}", who, status);
|
||||
|
||||
if self.peers.contains_key(&who) {
|
||||
error!(target: "sync", "Called on_sync_peer_connected with already connected peer {}", who);
|
||||
debug_assert!(false);
|
||||
return Err(())
|
||||
}
|
||||
|
||||
if status.genesis_hash != self.genesis_hash {
|
||||
log!(
|
||||
target: "sync",
|
||||
if self.important_peers.contains(&who) { Level::Warn } else { Level::Debug },
|
||||
"Peer is on different chain (our genesis: {} theirs: {})",
|
||||
self.genesis_hash, status.genesis_hash
|
||||
);
|
||||
self.peerset_handle.report_peer(who, rep::GENESIS_MISMATCH);
|
||||
self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC);
|
||||
|
||||
if self.boot_node_ids.contains(&who) {
|
||||
error!(
|
||||
target: "sync",
|
||||
"Bootnode with peer id `{}` is on a different chain (our genesis: {} theirs: {})",
|
||||
who,
|
||||
self.genesis_hash,
|
||||
status.genesis_hash,
|
||||
);
|
||||
}
|
||||
|
||||
return Err(())
|
||||
}
|
||||
|
||||
if self.roles.is_light() {
|
||||
// we're not interested in light peers
|
||||
if status.roles.is_light() {
|
||||
debug!(target: "sync", "Peer {} is unable to serve light requests", who);
|
||||
self.peerset_handle.report_peer(who, rep::BAD_ROLE);
|
||||
self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC);
|
||||
return Err(())
|
||||
}
|
||||
|
||||
// we don't interested in peers that are far behind us
|
||||
let self_best_block = self.chain.info().best_number;
|
||||
let blocks_difference = self_best_block
|
||||
.checked_sub(&status.best_number)
|
||||
.unwrap_or_else(Zero::zero)
|
||||
.saturated_into::<u64>();
|
||||
if blocks_difference > LIGHT_MAXIMAL_BLOCKS_DIFFERENCE {
|
||||
debug!(target: "sync", "Peer {} is far behind us and will unable to serve light requests", who);
|
||||
self.peerset_handle.report_peer(who, rep::PEER_BEHIND_US_LIGHT);
|
||||
self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC);
|
||||
return Err(())
|
||||
}
|
||||
}
|
||||
|
||||
let no_slot_peer = self.default_peers_set_no_slot_peers.contains(&who);
|
||||
let this_peer_reserved_slot: usize = if no_slot_peer { 1 } else { 0 };
|
||||
|
||||
if status.roles.is_full() &&
|
||||
self.chain_sync.num_peers() >=
|
||||
self.default_peers_set_num_full +
|
||||
self.default_peers_set_no_slot_connected_peers.len() +
|
||||
this_peer_reserved_slot
|
||||
{
|
||||
debug!(target: "sync", "Too many full nodes, rejecting {}", who);
|
||||
self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC);
|
||||
return Err(())
|
||||
}
|
||||
|
||||
if status.roles.is_light() &&
|
||||
(self.peers.len() - self.chain_sync.num_peers()) >= self.default_peers_set_num_light
|
||||
{
|
||||
// Make sure that not all slots are occupied by light clients.
|
||||
debug!(target: "sync", "Too many light nodes, rejecting {}", who);
|
||||
self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC);
|
||||
return Err(())
|
||||
}
|
||||
|
||||
let peer = Peer {
|
||||
info: PeerInfo {
|
||||
roles: status.roles,
|
||||
best_hash: status.best_hash,
|
||||
best_number: status.best_number,
|
||||
},
|
||||
known_blocks: LruHashSet::new(
|
||||
NonZeroUsize::new(MAX_KNOWN_BLOCKS).expect("Constant is nonzero"),
|
||||
),
|
||||
};
|
||||
|
||||
let req = if peer.info.roles.is_full() {
|
||||
match self.chain_sync.new_peer(who, peer.info.best_hash, peer.info.best_number) {
|
||||
Ok(req) => req,
|
||||
Err(BadPeer(id, repu)) => {
|
||||
self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC);
|
||||
self.peerset_handle.report_peer(id, repu);
|
||||
return Err(())
|
||||
},
|
||||
}
|
||||
/// Set handshake for the notification protocol.
|
||||
pub fn set_notification_handshake(&mut self, protocol: ProtocolName, handshake: Vec<u8>) {
|
||||
if let Some(index) = self.notification_protocols.iter().position(|p| *p == protocol) {
|
||||
self.behaviour
|
||||
.set_notif_protocol_handshake(sc_peerset::SetId::from(index), handshake);
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
debug!(target: "sync", "Connected {}", who);
|
||||
|
||||
self.peers.insert(who, peer);
|
||||
if no_slot_peer {
|
||||
self.default_peers_set_no_slot_connected_peers.insert(who);
|
||||
error!(
|
||||
target: "sub-libp2p",
|
||||
"set_notification_handshake with unknown protocol: {}",
|
||||
protocol
|
||||
);
|
||||
}
|
||||
self.pending_messages
|
||||
.push_back(CustomMessageOutcome::PeerNewBest(who, status.best_number));
|
||||
|
||||
if let Some(req) = req {
|
||||
self.chain_sync.send_block_request(who, req);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn announce_block(&mut self, hash: B::Hash, data: Option<Vec<u8>>) {
|
||||
let header = match self.chain.header(hash) {
|
||||
Ok(Some(header)) => header,
|
||||
Ok(None) => {
|
||||
warn!("Trying to announce unknown block: {}", hash);
|
||||
return
|
||||
},
|
||||
Err(e) => {
|
||||
warn!("Error reading block header {}: {}", hash, e);
|
||||
return
|
||||
},
|
||||
};
|
||||
|
||||
// don't announce genesis block since it will be ignored
|
||||
if header.number().is_zero() {
|
||||
return
|
||||
}
|
||||
|
||||
let is_best = self.chain.info().best_hash == hash;
|
||||
debug!(target: "sync", "Reannouncing block {:?} is_best: {}", hash, is_best);
|
||||
|
||||
let data = data
|
||||
.or_else(|| self.block_announce_data_cache.get(&hash).cloned())
|
||||
.unwrap_or_default();
|
||||
|
||||
for (who, ref mut peer) in self.peers.iter_mut() {
|
||||
let inserted = peer.known_blocks.insert(hash);
|
||||
if inserted {
|
||||
trace!(target: "sync", "Announcing block {:?} to {}", hash, who);
|
||||
let message = BlockAnnounce {
|
||||
header: header.clone(),
|
||||
state: if is_best { Some(BlockState::Best) } else { Some(BlockState::Normal) },
|
||||
data: Some(data.clone()),
|
||||
};
|
||||
|
||||
self.behaviour
|
||||
.write_notification(who, HARDCODED_PEERSETS_SYNC, message.encode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Push a block announce validation.
|
||||
///
|
||||
/// It is required that [`ChainSync::poll_block_announce_validation`] is
|
||||
/// called later to check for finished validations. The result of the validation
|
||||
/// needs to be passed to [`Protocol::process_block_announce_validation_result`]
|
||||
/// to finish the processing.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This will internally create a future, but this future will not be registered
|
||||
/// in the task before being polled once. So, it is required to call
|
||||
/// [`ChainSync::poll_block_announce_validation`] to ensure that the future is
|
||||
/// registered properly and will wake up the task when being ready.
|
||||
fn push_block_announce_validation(&mut self, who: PeerId, announce: BlockAnnounce<B::Header>) {
|
||||
let hash = announce.header.hash();
|
||||
|
||||
let peer = match self.peers.get_mut(&who) {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
log::error!(target: "sync", "Received block announce from disconnected peer {}", who);
|
||||
debug_assert!(false);
|
||||
return
|
||||
},
|
||||
};
|
||||
|
||||
peer.known_blocks.insert(hash);
|
||||
|
||||
let is_best = match announce.state.unwrap_or(BlockState::Best) {
|
||||
BlockState::Best => true,
|
||||
BlockState::Normal => false,
|
||||
};
|
||||
|
||||
if peer.info.roles.is_full() {
|
||||
self.chain_sync.push_block_announce_validation(who, hash, announce, is_best);
|
||||
}
|
||||
}
|
||||
|
||||
/// Process the result of the block announce validation.
|
||||
fn process_block_announce_validation_result(
|
||||
&mut self,
|
||||
validation_result: PollBlockAnnounceValidation<B::Header>,
|
||||
) -> CustomMessageOutcome<B> {
|
||||
let (header, is_best, who) = match validation_result {
|
||||
PollBlockAnnounceValidation::Skip => return CustomMessageOutcome::None,
|
||||
PollBlockAnnounceValidation::Nothing { is_best, who, announce } => {
|
||||
self.update_peer_info(&who);
|
||||
|
||||
if let Some(data) = announce.data {
|
||||
if !data.is_empty() {
|
||||
self.block_announce_data_cache.put(announce.header.hash(), data);
|
||||
}
|
||||
}
|
||||
|
||||
// `on_block_announce` returns `OnBlockAnnounce::ImportHeader`
|
||||
// when we have all data required to import the block
|
||||
// in the BlockAnnounce message. This is only when:
|
||||
// 1) we're on light client;
|
||||
// AND
|
||||
// 2) parent block is already imported and not pruned.
|
||||
if is_best {
|
||||
return CustomMessageOutcome::PeerNewBest(who, *announce.header.number())
|
||||
} else {
|
||||
return CustomMessageOutcome::None
|
||||
}
|
||||
},
|
||||
PollBlockAnnounceValidation::ImportHeader { announce, is_best, who } => {
|
||||
self.update_peer_info(&who);
|
||||
|
||||
if let Some(data) = announce.data {
|
||||
if !data.is_empty() {
|
||||
self.block_announce_data_cache.put(announce.header.hash(), data);
|
||||
}
|
||||
}
|
||||
|
||||
(announce.header, is_best, who)
|
||||
},
|
||||
PollBlockAnnounceValidation::Failure { who, disconnect } => {
|
||||
if disconnect {
|
||||
self.behaviour.disconnect_peer(&who, HARDCODED_PEERSETS_SYNC);
|
||||
}
|
||||
|
||||
self.report_peer(who, rep::BAD_BLOCK_ANNOUNCEMENT);
|
||||
return CustomMessageOutcome::None
|
||||
},
|
||||
};
|
||||
|
||||
let number = *header.number();
|
||||
|
||||
// to import header from announced block let's construct response to request that normally
|
||||
// would have been sent over network (but it is not in our case)
|
||||
let blocks_to_import = self.chain_sync.on_block_data(
|
||||
&who,
|
||||
None,
|
||||
BlockResponse::<B> {
|
||||
id: 0,
|
||||
blocks: vec![BlockData::<B> {
|
||||
hash: header.hash(),
|
||||
header: Some(header),
|
||||
body: None,
|
||||
indexed_body: None,
|
||||
receipt: None,
|
||||
message_queue: None,
|
||||
justification: None,
|
||||
justifications: None,
|
||||
}],
|
||||
},
|
||||
);
|
||||
self.chain_sync.process_block_response_data(blocks_to_import);
|
||||
|
||||
if is_best {
|
||||
self.pending_messages.push_back(CustomMessageOutcome::PeerNewBest(who, number));
|
||||
}
|
||||
|
||||
CustomMessageOutcome::None
|
||||
}
|
||||
|
||||
/// Call this when a block has been finalized. The sync layer may have some additional
|
||||
/// requesting to perform.
|
||||
pub fn on_block_finalized(&mut self, hash: B::Hash, header: &B::Header) {
|
||||
self.chain_sync.on_block_finalized(&hash, *header.number())
|
||||
}
|
||||
|
||||
/// Set whether the syncing peers set is in reserved-only mode.
|
||||
@@ -884,41 +332,12 @@ where
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn report_metrics(&self) {
|
||||
if let Some(metrics) = &self.metrics {
|
||||
let n = u64::try_from(self.peers.len()).unwrap_or(std::u64::MAX);
|
||||
metrics.peers.set(n);
|
||||
|
||||
let m = self.chain_sync.metrics();
|
||||
|
||||
metrics.fork_targets.set(m.fork_targets.into());
|
||||
metrics.queued_blocks.set(m.queued_blocks.into());
|
||||
|
||||
metrics
|
||||
.justifications
|
||||
.with_label_values(&["pending"])
|
||||
.set(m.justifications.pending_requests.into());
|
||||
metrics
|
||||
.justifications
|
||||
.with_label_values(&["active"])
|
||||
.set(m.justifications.active_requests.into());
|
||||
metrics
|
||||
.justifications
|
||||
.with_label_values(&["failed"])
|
||||
.set(m.justifications.failed_requests.into());
|
||||
metrics
|
||||
.justifications
|
||||
.with_label_values(&["importing"])
|
||||
.set(m.justifications.importing_requests.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Outcome of an incoming custom message.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub enum CustomMessageOutcome<B: BlockT> {
|
||||
pub enum CustomMessageOutcome {
|
||||
/// Notification protocols have been opened with a remote.
|
||||
NotificationStreamOpened {
|
||||
remote: PeerId,
|
||||
@@ -926,6 +345,7 @@ pub enum CustomMessageOutcome<B: BlockT> {
|
||||
/// See [`crate::Event::NotificationStreamOpened::negotiated_fallback`].
|
||||
negotiated_fallback: Option<ProtocolName>,
|
||||
roles: Roles,
|
||||
received_handshake: Vec<u8>,
|
||||
notifications_sink: NotificationsSink,
|
||||
},
|
||||
/// The [`NotificationsSink`] of some notification protocols need an update.
|
||||
@@ -935,31 +355,16 @@ pub enum CustomMessageOutcome<B: BlockT> {
|
||||
notifications_sink: NotificationsSink,
|
||||
},
|
||||
/// Notification protocols have been closed with a remote.
|
||||
NotificationStreamClosed {
|
||||
remote: PeerId,
|
||||
protocol: ProtocolName,
|
||||
},
|
||||
NotificationStreamClosed { remote: PeerId, protocol: ProtocolName },
|
||||
/// Messages have been received on one or more notifications protocols.
|
||||
NotificationsReceived {
|
||||
remote: PeerId,
|
||||
messages: Vec<(ProtocolName, Bytes)>,
|
||||
},
|
||||
/// Peer has a reported a new head of chain.
|
||||
PeerNewBest(PeerId, NumberFor<B>),
|
||||
NotificationsReceived { remote: PeerId, messages: Vec<(ProtocolName, Bytes)> },
|
||||
/// Now connected to a new peer for syncing purposes.
|
||||
SyncConnected(PeerId),
|
||||
/// No longer connected to a peer for syncing purposes.
|
||||
SyncDisconnected(PeerId),
|
||||
None,
|
||||
}
|
||||
|
||||
impl<B, Client> NetworkBehaviour for Protocol<B, Client>
|
||||
where
|
||||
B: BlockT,
|
||||
Client: HeaderBackend<B> + 'static,
|
||||
{
|
||||
impl<B: BlockT> NetworkBehaviour for Protocol<B> {
|
||||
type ConnectionHandler = <Notifications as NetworkBehaviour>::ConnectionHandler;
|
||||
type OutEvent = CustomMessageOutcome<B>;
|
||||
type OutEvent = CustomMessageOutcome;
|
||||
|
||||
fn new_handler(&mut self) -> Self::ConnectionHandler {
|
||||
self.behaviour.new_handler()
|
||||
@@ -994,25 +399,6 @@ where
|
||||
return Poll::Ready(NetworkBehaviourAction::GenerateEvent(message))
|
||||
}
|
||||
|
||||
// Advance the state of `ChainSync`
|
||||
//
|
||||
// Process any received requests received from `NetworkService` and
|
||||
// check if there is any block announcement validation finished.
|
||||
while let Poll::Ready(result) = self.chain_sync.poll(cx) {
|
||||
match self.process_block_announce_validation_result(result) {
|
||||
CustomMessageOutcome::None => {},
|
||||
outcome => self.pending_messages.push_back(outcome),
|
||||
}
|
||||
}
|
||||
|
||||
while let Poll::Ready(Some(())) = self.tick_timeout.poll_next_unpin(cx) {
|
||||
self.tick();
|
||||
}
|
||||
|
||||
if let Some(message) = self.pending_messages.pop_front() {
|
||||
return Poll::Ready(NetworkBehaviourAction::GenerateEvent(message))
|
||||
}
|
||||
|
||||
let event = match self.behaviour.poll(cx, params) {
|
||||
Poll::Pending => return Poll::Pending,
|
||||
Poll::Ready(NetworkBehaviourAction::GenerateEvent(ev)) => ev,
|
||||
@@ -1045,17 +431,22 @@ where
|
||||
// announces substream.
|
||||
match <Message<B> as DecodeAll>::decode_all(&mut &received_handshake[..]) {
|
||||
Ok(GenericMessage::Status(handshake)) => {
|
||||
let handshake = BlockAnnouncesHandshake {
|
||||
let roles = handshake.roles;
|
||||
let handshake = BlockAnnouncesHandshake::<B> {
|
||||
roles: handshake.roles,
|
||||
best_number: handshake.best_number,
|
||||
best_hash: handshake.best_hash,
|
||||
genesis_hash: handshake.genesis_hash,
|
||||
};
|
||||
self.peers.insert(peer_id, roles);
|
||||
|
||||
if self.on_sync_peer_connected(peer_id, handshake).is_ok() {
|
||||
CustomMessageOutcome::SyncConnected(peer_id)
|
||||
} else {
|
||||
CustomMessageOutcome::None
|
||||
CustomMessageOutcome::NotificationStreamOpened {
|
||||
remote: peer_id,
|
||||
protocol: self.notification_protocols[usize::from(set_id)].clone(),
|
||||
negotiated_fallback,
|
||||
received_handshake: handshake.encode(),
|
||||
roles,
|
||||
notifications_sink,
|
||||
}
|
||||
},
|
||||
Ok(msg) => {
|
||||
@@ -1073,14 +464,21 @@ where
|
||||
&mut &received_handshake[..],
|
||||
) {
|
||||
Ok(handshake) => {
|
||||
if self.on_sync_peer_connected(peer_id, handshake).is_ok() {
|
||||
CustomMessageOutcome::SyncConnected(peer_id)
|
||||
} else {
|
||||
CustomMessageOutcome::None
|
||||
let roles = handshake.roles;
|
||||
self.peers.insert(peer_id, roles);
|
||||
|
||||
CustomMessageOutcome::NotificationStreamOpened {
|
||||
remote: peer_id,
|
||||
protocol: self.notification_protocols[usize::from(set_id)]
|
||||
.clone(),
|
||||
negotiated_fallback,
|
||||
received_handshake,
|
||||
roles,
|
||||
notifications_sink,
|
||||
}
|
||||
},
|
||||
Err(err2) => {
|
||||
debug!(
|
||||
log::debug!(
|
||||
target: "sync",
|
||||
"Couldn't decode handshake sent by {}: {:?}: {} & {}",
|
||||
peer_id,
|
||||
@@ -1104,9 +502,10 @@ where
|
||||
protocol: self.notification_protocols[usize::from(set_id)].clone(),
|
||||
negotiated_fallback,
|
||||
roles,
|
||||
received_handshake,
|
||||
notifications_sink,
|
||||
},
|
||||
(Err(_), Some(peer)) if received_handshake.is_empty() => {
|
||||
(Err(_), Some(roles)) if received_handshake.is_empty() => {
|
||||
// As a convenience, we allow opening substreams for "external"
|
||||
// notification protocols with an empty handshake. This fetches the
|
||||
// roles from the locally-known roles.
|
||||
@@ -1115,7 +514,8 @@ where
|
||||
remote: peer_id,
|
||||
protocol: self.notification_protocols[usize::from(set_id)].clone(),
|
||||
negotiated_fallback,
|
||||
roles: peer.info.roles,
|
||||
roles: *roles,
|
||||
received_handshake,
|
||||
notifications_sink,
|
||||
}
|
||||
},
|
||||
@@ -1124,15 +524,14 @@ where
|
||||
self.bad_handshake_substreams.insert((peer_id, set_id));
|
||||
self.behaviour.disconnect_peer(&peer_id, set_id);
|
||||
self.peerset_handle.report_peer(peer_id, rep::BAD_MESSAGE);
|
||||
self.peers.remove(&peer_id);
|
||||
CustomMessageOutcome::None
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
NotificationsOut::CustomProtocolReplaced { peer_id, notifications_sink, set_id } =>
|
||||
if set_id == HARDCODED_PEERSETS_SYNC ||
|
||||
self.bad_handshake_substreams.contains(&(peer_id, set_id))
|
||||
{
|
||||
if self.bad_handshake_substreams.contains(&(peer_id, set_id)) {
|
||||
CustomMessageOutcome::None
|
||||
} else {
|
||||
CustomMessageOutcome::NotificationStreamReplaced {
|
||||
@@ -1142,19 +541,7 @@ where
|
||||
}
|
||||
},
|
||||
NotificationsOut::CustomProtocolClosed { peer_id, set_id } => {
|
||||
// Set number 0 is hardcoded the default set of peers we sync from.
|
||||
if set_id == HARDCODED_PEERSETS_SYNC {
|
||||
if self.on_sync_peer_disconnected(peer_id).is_ok() {
|
||||
CustomMessageOutcome::SyncDisconnected(peer_id)
|
||||
} else {
|
||||
log::trace!(
|
||||
target: "sync",
|
||||
"Disconnected peer which had earlier been refused by on_sync_peer_connected {}",
|
||||
peer_id
|
||||
);
|
||||
CustomMessageOutcome::None
|
||||
}
|
||||
} else if self.bad_handshake_substreams.remove(&(peer_id, set_id)) {
|
||||
if self.bad_handshake_substreams.remove(&(peer_id, set_id)) {
|
||||
// The substream that has just been closed had been opened with a bad
|
||||
// handshake. The outer layers have never received an opening event about this
|
||||
// substream, and consequently shouldn't receive a closing event either.
|
||||
@@ -1166,45 +553,20 @@ where
|
||||
}
|
||||
}
|
||||
},
|
||||
NotificationsOut::Notification { peer_id, set_id, message } => match set_id {
|
||||
HARDCODED_PEERSETS_SYNC if self.peers.contains_key(&peer_id) => {
|
||||
if let Ok(announce) = BlockAnnounce::decode(&mut message.as_ref()) {
|
||||
self.push_block_announce_validation(peer_id, announce);
|
||||
|
||||
// Make sure that the newly added block announce validation future was
|
||||
// polled once to be registered in the task.
|
||||
if let Poll::Ready(res) = self.chain_sync.poll_block_announce_validation(cx)
|
||||
{
|
||||
self.process_block_announce_validation_result(res)
|
||||
} else {
|
||||
CustomMessageOutcome::None
|
||||
}
|
||||
} else {
|
||||
warn!(target: "sub-libp2p", "Failed to decode block announce");
|
||||
CustomMessageOutcome::None
|
||||
}
|
||||
},
|
||||
HARDCODED_PEERSETS_SYNC => {
|
||||
trace!(
|
||||
target: "sync",
|
||||
"Received sync for peer earlier refused by sync layer: {}",
|
||||
peer_id
|
||||
);
|
||||
NotificationsOut::Notification { peer_id, set_id, message } => {
|
||||
if self.bad_handshake_substreams.contains(&(peer_id, set_id)) {
|
||||
CustomMessageOutcome::None
|
||||
},
|
||||
_ if self.bad_handshake_substreams.contains(&(peer_id, set_id)) =>
|
||||
CustomMessageOutcome::None,
|
||||
_ => {
|
||||
} else {
|
||||
let protocol_name = self.notification_protocols[usize::from(set_id)].clone();
|
||||
CustomMessageOutcome::NotificationsReceived {
|
||||
remote: peer_id,
|
||||
messages: vec![(protocol_name, message.freeze())],
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if !matches!(outcome, CustomMessageOutcome::<B>::None) {
|
||||
if !matches!(outcome, CustomMessageOutcome::None) {
|
||||
return Poll::Ready(NetworkBehaviourAction::GenerateEvent(outcome))
|
||||
}
|
||||
|
||||
|
||||
@@ -538,48 +538,6 @@ impl Notifications {
|
||||
self.peerset.reserved_peers(set_id)
|
||||
}
|
||||
|
||||
/// Sends a notification to a peer.
|
||||
///
|
||||
/// Has no effect if the custom protocol is not open with the given peer.
|
||||
///
|
||||
/// Also note that even if we have a valid open substream, it may in fact be already closed
|
||||
/// without us knowing, in which case the packet will not be received.
|
||||
///
|
||||
/// The `fallback` parameter is used for backwards-compatibility reason if the remote doesn't
|
||||
/// support our protocol. One needs to pass the equivalent of what would have been passed
|
||||
/// with `send_packet`.
|
||||
pub fn write_notification(
|
||||
&mut self,
|
||||
target: &PeerId,
|
||||
set_id: sc_peerset::SetId,
|
||||
message: impl Into<Vec<u8>>,
|
||||
) {
|
||||
let notifs_sink = match self.peers.get(&(*target, set_id)).and_then(|p| p.get_open()) {
|
||||
None => {
|
||||
trace!(
|
||||
target: "sub-libp2p",
|
||||
"Tried to sent notification to {:?} without an open channel.",
|
||||
target,
|
||||
);
|
||||
return
|
||||
},
|
||||
Some(sink) => sink,
|
||||
};
|
||||
|
||||
let message = message.into();
|
||||
|
||||
trace!(
|
||||
target: "sub-libp2p",
|
||||
"External API => Notification({:?}, {:?}, {} bytes)",
|
||||
target,
|
||||
set_id,
|
||||
message.len(),
|
||||
);
|
||||
trace!(target: "sub-libp2p", "Handler({:?}) <= Sync notification", target);
|
||||
|
||||
notifs_sink.send_sync_notification(message);
|
||||
}
|
||||
|
||||
/// Returns the state of the peerset manager, for debugging purposes.
|
||||
pub fn peerset_debug_info(&mut self) -> serde_json::Value {
|
||||
self.peerset.debug_info()
|
||||
@@ -3058,7 +3016,13 @@ mod tests {
|
||||
panic!("invalid state");
|
||||
}
|
||||
|
||||
notif.write_notification(&peer, set_id, vec![1, 3, 3, 7]);
|
||||
notif
|
||||
.peers
|
||||
.get(&(peer, set_id))
|
||||
.unwrap()
|
||||
.get_open()
|
||||
.unwrap()
|
||||
.send_sync_notification(vec![1, 3, 3, 7]);
|
||||
assert_eq!(conn_yielder.get_next_event(peer, set_id.into()).await, Some(vec![1, 3, 3, 7]));
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ use crate::{
|
||||
network_state::{
|
||||
NetworkState, NotConnectedPeer as NetworkStateNotConnectedPeer, Peer as NetworkStatePeer,
|
||||
},
|
||||
protocol::{self, NotificationsSink, NotifsHandlerError, PeerInfo, Protocol, Ready},
|
||||
transport, ChainSyncInterface, ReputationChange,
|
||||
protocol::{self, NotificationsSink, NotifsHandlerError, Protocol, Ready},
|
||||
transport, ReputationChange,
|
||||
};
|
||||
|
||||
use futures::{channel::oneshot, prelude::*};
|
||||
@@ -65,17 +65,16 @@ use sc_network_common::{
|
||||
request_responses::{IfDisconnected, RequestFailure},
|
||||
service::{
|
||||
NetworkDHTProvider, NetworkEventStream, NetworkNotification, NetworkPeers, NetworkSigner,
|
||||
NetworkStateInfo, NetworkStatus, NetworkStatusProvider, NetworkSyncForkRequest,
|
||||
NetworkStateInfo, NetworkStatus, NetworkStatusProvider,
|
||||
NotificationSender as NotificationSenderT, NotificationSenderError,
|
||||
NotificationSenderReady as NotificationSenderReadyT, Signature, SigningError,
|
||||
},
|
||||
sync::SyncStatus,
|
||||
ExHashT,
|
||||
};
|
||||
use sc_peerset::PeersetHandle;
|
||||
use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender};
|
||||
use sp_blockchain::HeaderBackend;
|
||||
use sp_runtime::traits::{Block as BlockT, NumberFor, Zero};
|
||||
use sp_runtime::traits::{Block as BlockT, Zero};
|
||||
use std::{
|
||||
cmp,
|
||||
collections::{HashMap, HashSet},
|
||||
@@ -85,7 +84,7 @@ use std::{
|
||||
pin::Pin,
|
||||
str,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
@@ -98,7 +97,7 @@ mod out_events;
|
||||
mod tests;
|
||||
|
||||
pub use libp2p::identity::{error::DecodingError, Keypair, PublicKey};
|
||||
use sc_network_common::service::{NetworkBlock, NetworkRequest};
|
||||
use sc_network_common::service::NetworkRequest;
|
||||
|
||||
/// Custom error that can be produced by the [`ConnectionHandler`] of the [`NetworkBehaviour`].
|
||||
/// Used as a template parameter of [`SwarmEvent`] below.
|
||||
@@ -114,8 +113,6 @@ pub struct NetworkService<B: BlockT + 'static, H: ExHashT> {
|
||||
external_addresses: Arc<Mutex<Vec<Multiaddr>>>,
|
||||
/// Listen addresses. Do **NOT** include a trailing `/p2p/` with our `PeerId`.
|
||||
listen_addresses: Arc<Mutex<Vec<Multiaddr>>>,
|
||||
/// Are we actively catching up with the chain?
|
||||
is_major_syncing: Arc<AtomicBool>,
|
||||
/// Local copy of the `PeerId` of the local node.
|
||||
local_peer_id: PeerId,
|
||||
/// The `KeyPair` that defines the `PeerId` of the local node.
|
||||
@@ -126,9 +123,7 @@ pub struct NetworkService<B: BlockT + 'static, H: ExHashT> {
|
||||
/// nodes it should be connected to or not.
|
||||
peerset: PeersetHandle,
|
||||
/// Channel that sends messages to the actual worker.
|
||||
to_worker: TracingUnboundedSender<ServiceToWorkerMsg<B>>,
|
||||
/// Interface that can be used to delegate calls to `ChainSync`
|
||||
chain_sync_service: Box<dyn ChainSyncInterface<B>>,
|
||||
to_worker: TracingUnboundedSender<ServiceToWorkerMsg>,
|
||||
/// For each peer and protocol combination, an object that allows sending notifications to
|
||||
/// that peer. Updated by the [`NetworkWorker`].
|
||||
peers_notifications_sinks: Arc<Mutex<HashMap<(PeerId, ProtocolName), NotificationsSink>>>,
|
||||
@@ -138,20 +133,23 @@ pub struct NetworkService<B: BlockT + 'static, H: ExHashT> {
|
||||
/// Marker to pin the `H` generic. Serves no purpose except to not break backwards
|
||||
/// compatibility.
|
||||
_marker: PhantomData<H>,
|
||||
/// Marker for block type
|
||||
_block: PhantomData<B>,
|
||||
}
|
||||
|
||||
impl<B, H, Client> NetworkWorker<B, H, Client>
|
||||
impl<B, H> NetworkWorker<B, H>
|
||||
where
|
||||
B: BlockT + 'static,
|
||||
H: ExHashT,
|
||||
Client: HeaderBackend<B> + 'static,
|
||||
{
|
||||
/// Creates the network service.
|
||||
///
|
||||
/// Returns a `NetworkWorker` that implements `Future` and must be regularly polled in order
|
||||
/// for the network processing to advance. From it, you can extract a `NetworkService` using
|
||||
/// `worker.service()`. The `NetworkService` can be shared through the codebase.
|
||||
pub fn new(mut params: Params<B, Client>) -> Result<Self, Error> {
|
||||
pub fn new<Client: HeaderBackend<B> + 'static>(
|
||||
mut params: Params<Client>,
|
||||
) -> Result<Self, Error> {
|
||||
// Private and public keys configuration.
|
||||
let local_identity = params.network_config.node_key.clone().into_keypair()?;
|
||||
let local_public = local_identity.public();
|
||||
@@ -230,10 +228,7 @@ where
|
||||
|
||||
let (protocol, peerset_handle, mut known_addresses) = Protocol::new(
|
||||
From::from(¶ms.role),
|
||||
params.chain.clone(),
|
||||
¶ms.network_config,
|
||||
params.metrics_registry.as_ref(),
|
||||
params.chain_sync,
|
||||
params.block_announce_config,
|
||||
)?;
|
||||
|
||||
@@ -268,10 +263,9 @@ where
|
||||
})?;
|
||||
|
||||
let num_connected = Arc::new(AtomicUsize::new(0));
|
||||
let is_major_syncing = Arc::new(AtomicBool::new(false));
|
||||
|
||||
// Build the swarm.
|
||||
let (mut swarm, bandwidth): (Swarm<Behaviour<B, Client>>, _) = {
|
||||
let (mut swarm, bandwidth): (Swarm<Behaviour<B>>, _) = {
|
||||
let user_agent = format!(
|
||||
"{} ({})",
|
||||
params.network_config.client_version, params.network_config.node_name
|
||||
@@ -418,7 +412,6 @@ where
|
||||
registry,
|
||||
MetricSources {
|
||||
bandwidth: bandwidth.clone(),
|
||||
major_syncing: is_major_syncing.clone(),
|
||||
connected_peers: num_connected.clone(),
|
||||
},
|
||||
)?),
|
||||
@@ -427,14 +420,14 @@ where
|
||||
|
||||
// Listen on multiaddresses.
|
||||
for addr in ¶ms.network_config.listen_addresses {
|
||||
if let Err(err) = Swarm::<Behaviour<B, Client>>::listen_on(&mut swarm, addr.clone()) {
|
||||
if let Err(err) = Swarm::<Behaviour<B>>::listen_on(&mut swarm, addr.clone()) {
|
||||
warn!(target: "sub-libp2p", "Can't listen on {} because: {:?}", addr, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Add external addresses.
|
||||
for addr in ¶ms.network_config.public_addresses {
|
||||
Swarm::<Behaviour<B, Client>>::add_external_address(
|
||||
Swarm::<Behaviour<B>>::add_external_address(
|
||||
&mut swarm,
|
||||
addr.clone(),
|
||||
AddressScore::Infinite,
|
||||
@@ -450,24 +443,22 @@ where
|
||||
external_addresses: external_addresses.clone(),
|
||||
listen_addresses: listen_addresses.clone(),
|
||||
num_connected: num_connected.clone(),
|
||||
is_major_syncing: is_major_syncing.clone(),
|
||||
peerset: peerset_handle,
|
||||
local_peer_id,
|
||||
local_identity,
|
||||
to_worker,
|
||||
chain_sync_service: params.chain_sync_service,
|
||||
peers_notifications_sinks: peers_notifications_sinks.clone(),
|
||||
notifications_sizes_metric: metrics
|
||||
.as_ref()
|
||||
.map(|metrics| metrics.notifications_sizes.clone()),
|
||||
_marker: PhantomData,
|
||||
_block: Default::default(),
|
||||
});
|
||||
|
||||
Ok(NetworkWorker {
|
||||
external_addresses,
|
||||
listen_addresses,
|
||||
num_connected,
|
||||
is_major_syncing,
|
||||
network_service: swarm,
|
||||
service,
|
||||
from_service,
|
||||
@@ -476,22 +467,16 @@ where
|
||||
metrics,
|
||||
boot_node_ids,
|
||||
_marker: Default::default(),
|
||||
_block: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// High-level network status information.
|
||||
pub fn status(&self) -> NetworkStatus<B> {
|
||||
let status = self.sync_state();
|
||||
pub fn status(&self) -> NetworkStatus {
|
||||
NetworkStatus {
|
||||
sync_state: status.state,
|
||||
best_seen_block: self.best_seen_block(),
|
||||
num_sync_peers: self.num_sync_peers(),
|
||||
num_connected_peers: self.num_connected_peers(),
|
||||
num_active_peers: self.num_active_peers(),
|
||||
total_bytes_inbound: self.total_bytes_inbound(),
|
||||
total_bytes_outbound: self.total_bytes_outbound(),
|
||||
state_sync: status.state_sync,
|
||||
warp_sync: status.warp_sync,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,42 +495,7 @@ where
|
||||
self.network_service.behaviour().user_protocol().num_connected_peers()
|
||||
}
|
||||
|
||||
/// Returns the number of peers we're connected to and that are being queried.
|
||||
pub fn num_active_peers(&self) -> usize {
|
||||
self.network_service.behaviour().user_protocol().num_active_peers()
|
||||
}
|
||||
|
||||
/// Current global sync state.
|
||||
pub fn sync_state(&self) -> SyncStatus<B> {
|
||||
self.network_service.behaviour().user_protocol().sync_state()
|
||||
}
|
||||
|
||||
/// Target sync block number.
|
||||
pub fn best_seen_block(&self) -> Option<NumberFor<B>> {
|
||||
self.network_service.behaviour().user_protocol().best_seen_block()
|
||||
}
|
||||
|
||||
/// Number of peers participating in syncing.
|
||||
pub fn num_sync_peers(&self) -> u32 {
|
||||
self.network_service.behaviour().user_protocol().num_sync_peers()
|
||||
}
|
||||
|
||||
/// Number of blocks in the import queue.
|
||||
pub fn num_queued_blocks(&self) -> u32 {
|
||||
self.network_service.behaviour().user_protocol().num_queued_blocks()
|
||||
}
|
||||
|
||||
/// Returns the number of downloaded blocks.
|
||||
pub fn num_downloaded_blocks(&self) -> usize {
|
||||
self.network_service.behaviour().user_protocol().num_downloaded_blocks()
|
||||
}
|
||||
|
||||
/// Number of active sync requests.
|
||||
pub fn num_sync_requests(&self) -> usize {
|
||||
self.network_service.behaviour().user_protocol().num_sync_requests()
|
||||
}
|
||||
|
||||
/// Adds an address known to a node.
|
||||
/// Adds an address for a node.
|
||||
pub fn add_known_address(&mut self, peer_id: PeerId, addr: Multiaddr) {
|
||||
self.network_service.behaviour_mut().add_known_address(peer_id, addr);
|
||||
}
|
||||
@@ -556,32 +506,16 @@ where
|
||||
&self.service
|
||||
}
|
||||
|
||||
/// You must call this when a new block is finalized by the client.
|
||||
pub fn on_block_finalized(&mut self, hash: B::Hash, header: B::Header) {
|
||||
self.network_service
|
||||
.behaviour_mut()
|
||||
.user_protocol_mut()
|
||||
.on_block_finalized(hash, &header);
|
||||
}
|
||||
|
||||
/// Inform the network service about new best imported block.
|
||||
pub fn new_best_block_imported(&mut self, hash: B::Hash, number: NumberFor<B>) {
|
||||
self.network_service
|
||||
.behaviour_mut()
|
||||
.user_protocol_mut()
|
||||
.new_best_block_imported(hash, number);
|
||||
}
|
||||
|
||||
/// Returns the local `PeerId`.
|
||||
pub fn local_peer_id(&self) -> &PeerId {
|
||||
Swarm::<Behaviour<B, Client>>::local_peer_id(&self.network_service)
|
||||
Swarm::<Behaviour<B>>::local_peer_id(&self.network_service)
|
||||
}
|
||||
|
||||
/// Returns the list of addresses we are listening on.
|
||||
///
|
||||
/// Does **NOT** include a trailing `/p2p/` with our `PeerId`.
|
||||
pub fn listen_addresses(&self) -> impl Iterator<Item = &Multiaddr> {
|
||||
Swarm::<Behaviour<B, Client>>::listeners(&self.network_service)
|
||||
Swarm::<Behaviour<B>>::listeners(&self.network_service)
|
||||
}
|
||||
|
||||
/// Get network state.
|
||||
@@ -661,7 +595,7 @@ where
|
||||
.collect()
|
||||
};
|
||||
|
||||
let peer_id = Swarm::<Behaviour<B, Client>>::local_peer_id(swarm).to_base58();
|
||||
let peer_id = Swarm::<Behaviour<B>>::local_peer_id(swarm).to_base58();
|
||||
let listened_addresses = swarm.listeners().cloned().collect();
|
||||
let external_addresses = swarm.external_addresses().map(|r| &r.addr).cloned().collect();
|
||||
|
||||
@@ -675,16 +609,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Get currently connected peers.
|
||||
pub fn peers_debug_info(&mut self) -> Vec<(PeerId, PeerInfo<B>)> {
|
||||
self.network_service
|
||||
.behaviour_mut()
|
||||
.user_protocol_mut()
|
||||
.peers_info()
|
||||
.map(|(id, info)| (*id, info.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Removes a `PeerId` from the list of reserved peers.
|
||||
pub fn remove_reserved_peer(&self, peer: PeerId) {
|
||||
self.service.remove_reserved_peer(peer);
|
||||
@@ -722,20 +646,6 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkService<B, H> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get connected peers debug information.
|
||||
///
|
||||
/// Returns an error if the `NetworkWorker` is no longer running.
|
||||
pub async fn peers_debug_info(&self) -> Result<Vec<(PeerId, PeerInfo<B>)>, ()> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
let _ = self
|
||||
.to_worker
|
||||
.unbounded_send(ServiceToWorkerMsg::PeersDebugInfo { pending_response: tx });
|
||||
|
||||
// The channel can only be closed if the network worker no longer exists.
|
||||
rx.await.map_err(|_| ())
|
||||
}
|
||||
|
||||
/// Get the list of reserved peers.
|
||||
///
|
||||
/// Returns an error if the `NetworkWorker` is no longer running.
|
||||
@@ -779,30 +689,6 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkService<B, H> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BlockT + 'static, H: ExHashT> sp_consensus::SyncOracle for NetworkService<B, H> {
|
||||
fn is_major_syncing(&self) -> bool {
|
||||
self.is_major_syncing.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn is_offline(&self) -> bool {
|
||||
self.num_connected.load(Ordering::Relaxed) == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BlockT, H: ExHashT> sc_consensus::JustificationSyncLink<B> for NetworkService<B, H> {
|
||||
/// Request a justification for the given block from the network.
|
||||
///
|
||||
/// On success, the justification will be passed to the import queue that was part at
|
||||
/// initialization as part of the configuration.
|
||||
fn request_justification(&self, hash: &B::Hash, number: NumberFor<B>) {
|
||||
let _ = self.chain_sync_service.request_justification(hash, number);
|
||||
}
|
||||
|
||||
fn clear_justification_requests(&self) {
|
||||
let _ = self.chain_sync_service.clear_justification_requests();
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, H> NetworkStateInfo for NetworkService<B, H>
|
||||
where
|
||||
B: sp_runtime::traits::Block,
|
||||
@@ -856,29 +742,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, H> NetworkSyncForkRequest<B::Hash, NumberFor<B>> for NetworkService<B, H>
|
||||
where
|
||||
B: BlockT + 'static,
|
||||
H: ExHashT,
|
||||
{
|
||||
/// Configure an explicit fork sync request.
|
||||
/// Note that this function should not be used for recent blocks.
|
||||
/// Sync should be able to download all the recent forks normally.
|
||||
/// `set_sync_fork_request` should only be used if external code detects that there's
|
||||
/// a stale fork missing.
|
||||
/// Passing empty `peers` set effectively removes the sync request.
|
||||
fn set_sync_fork_request(&self, peers: Vec<PeerId>, hash: B::Hash, number: NumberFor<B>) {
|
||||
self.chain_sync_service.set_sync_fork_request(peers, hash, number);
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<B, H> NetworkStatusProvider<B> for NetworkService<B, H>
|
||||
impl<B, H> NetworkStatusProvider for NetworkService<B, H>
|
||||
where
|
||||
B: BlockT + 'static,
|
||||
H: ExHashT,
|
||||
{
|
||||
async fn status(&self) -> Result<NetworkStatus<B>, ()> {
|
||||
async fn status(&self) -> Result<NetworkStatus, ()> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
let _ = self
|
||||
@@ -1125,6 +995,12 @@ where
|
||||
|
||||
Ok(Box::new(NotificationSender { sink, protocol_name: protocol, notification_size_metric }))
|
||||
}
|
||||
|
||||
fn set_notification_handshake(&self, protocol: ProtocolName, handshake: Vec<u8>) {
|
||||
let _ = self
|
||||
.to_worker
|
||||
.unbounded_send(ServiceToWorkerMsg::SetNotificationHandshake(protocol, handshake));
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
@@ -1171,22 +1047,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, H> NetworkBlock<B::Hash, NumberFor<B>> for NetworkService<B, H>
|
||||
where
|
||||
B: BlockT + 'static,
|
||||
H: ExHashT,
|
||||
{
|
||||
fn announce_block(&self, hash: B::Hash, data: Option<Vec<u8>>) {
|
||||
let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::AnnounceBlock(hash, data));
|
||||
}
|
||||
|
||||
fn new_best_block_imported(&self, hash: B::Hash, number: NumberFor<B>) {
|
||||
let _ = self
|
||||
.to_worker
|
||||
.unbounded_send(ServiceToWorkerMsg::NewBestBlockImported(hash, number));
|
||||
}
|
||||
}
|
||||
|
||||
/// A `NotificationSender` allows for sending notifications to a peer with a chosen protocol.
|
||||
#[must_use]
|
||||
pub struct NotificationSender {
|
||||
@@ -1257,8 +1117,7 @@ impl<'a> NotificationSenderReadyT for NotificationSenderReady<'a> {
|
||||
/// Messages sent from the `NetworkService` to the `NetworkWorker`.
|
||||
///
|
||||
/// Each entry corresponds to a method of `NetworkService`.
|
||||
enum ServiceToWorkerMsg<B: BlockT> {
|
||||
AnnounceBlock(B::Hash, Option<Vec<u8>>),
|
||||
enum ServiceToWorkerMsg {
|
||||
GetValue(KademliaKey),
|
||||
PutValue(KademliaKey, Vec<u8>),
|
||||
AddKnownAddress(PeerId, Multiaddr),
|
||||
@@ -1280,16 +1139,13 @@ enum ServiceToWorkerMsg<B: BlockT> {
|
||||
connect: IfDisconnected,
|
||||
},
|
||||
NetworkStatus {
|
||||
pending_response: oneshot::Sender<Result<NetworkStatus<B>, RequestFailure>>,
|
||||
pending_response: oneshot::Sender<Result<NetworkStatus, RequestFailure>>,
|
||||
},
|
||||
NetworkState {
|
||||
pending_response: oneshot::Sender<Result<NetworkState, RequestFailure>>,
|
||||
},
|
||||
DisconnectPeer(PeerId, ProtocolName),
|
||||
NewBestBlockImported(B::Hash, NumberFor<B>),
|
||||
PeersDebugInfo {
|
||||
pending_response: oneshot::Sender<Vec<(PeerId, PeerInfo<B>)>>,
|
||||
},
|
||||
SetNotificationHandshake(ProtocolName, Vec<u8>),
|
||||
ReservedPeers {
|
||||
pending_response: oneshot::Sender<Vec<PeerId>>,
|
||||
},
|
||||
@@ -1299,11 +1155,10 @@ enum ServiceToWorkerMsg<B: BlockT> {
|
||||
///
|
||||
/// You are encouraged to poll this in a separate background thread or task.
|
||||
#[must_use = "The NetworkWorker must be polled in order for the network to advance"]
|
||||
pub struct NetworkWorker<B, H, Client>
|
||||
pub struct NetworkWorker<B, H>
|
||||
where
|
||||
B: BlockT + 'static,
|
||||
H: ExHashT,
|
||||
Client: HeaderBackend<B> + 'static,
|
||||
{
|
||||
/// Updated by the `NetworkWorker` and loaded by the `NetworkService`.
|
||||
external_addresses: Arc<Mutex<Vec<Multiaddr>>>,
|
||||
@@ -1311,14 +1166,12 @@ where
|
||||
listen_addresses: Arc<Mutex<Vec<Multiaddr>>>,
|
||||
/// Updated by the `NetworkWorker` and loaded by the `NetworkService`.
|
||||
num_connected: Arc<AtomicUsize>,
|
||||
/// Updated by the `NetworkWorker` and loaded by the `NetworkService`.
|
||||
is_major_syncing: Arc<AtomicBool>,
|
||||
/// The network service that can be extracted and shared through the codebase.
|
||||
service: Arc<NetworkService<B, H>>,
|
||||
/// The *actual* network.
|
||||
network_service: Swarm<Behaviour<B, Client>>,
|
||||
network_service: Swarm<Behaviour<B>>,
|
||||
/// Messages from the [`NetworkService`] that must be processed.
|
||||
from_service: TracingUnboundedReceiver<ServiceToWorkerMsg<B>>,
|
||||
from_service: TracingUnboundedReceiver<ServiceToWorkerMsg>,
|
||||
/// Senders for events that happen on the network.
|
||||
event_streams: out_events::OutChannels,
|
||||
/// Prometheus network metrics.
|
||||
@@ -1331,13 +1184,14 @@ where
|
||||
/// Marker to pin the `H` generic. Serves no purpose except to not break backwards
|
||||
/// compatibility.
|
||||
_marker: PhantomData<H>,
|
||||
/// Marker for block type
|
||||
_block: PhantomData<B>,
|
||||
}
|
||||
|
||||
impl<B, H, Client> NetworkWorker<B, H, Client>
|
||||
impl<B, H> NetworkWorker<B, H>
|
||||
where
|
||||
B: BlockT + 'static,
|
||||
H: ExHashT,
|
||||
Client: HeaderBackend<B> + 'static,
|
||||
{
|
||||
/// Run the network.
|
||||
pub async fn run(mut self) {
|
||||
@@ -1364,10 +1218,9 @@ where
|
||||
},
|
||||
};
|
||||
|
||||
// Update the variables shared with the `NetworkService`.
|
||||
let num_connected_peers =
|
||||
self.network_service.behaviour_mut().user_protocol_mut().num_connected_peers();
|
||||
|
||||
// Update the variables shared with the `NetworkService`.
|
||||
self.num_connected.store(num_connected_peers, Ordering::Relaxed);
|
||||
{
|
||||
let external_addresses =
|
||||
@@ -1379,16 +1232,6 @@ where
|
||||
*self.listen_addresses.lock() = listen_addresses;
|
||||
}
|
||||
|
||||
let is_major_syncing = self
|
||||
.network_service
|
||||
.behaviour_mut()
|
||||
.user_protocol_mut()
|
||||
.sync_state()
|
||||
.state
|
||||
.is_major_syncing();
|
||||
|
||||
self.is_major_syncing.store(is_major_syncing, Ordering::Relaxed);
|
||||
|
||||
if let Some(metrics) = self.metrics.as_ref() {
|
||||
if let Some(buckets) = self.network_service.behaviour_mut().num_entries_per_kbucket() {
|
||||
for (lower_ilog2_bucket_bound, num_entries) in buckets {
|
||||
@@ -1420,13 +1263,8 @@ where
|
||||
}
|
||||
|
||||
/// Process the next message coming from the `NetworkService`.
|
||||
fn handle_worker_message(&mut self, msg: ServiceToWorkerMsg<B>) {
|
||||
fn handle_worker_message(&mut self, msg: ServiceToWorkerMsg) {
|
||||
match msg {
|
||||
ServiceToWorkerMsg::AnnounceBlock(hash, data) => self
|
||||
.network_service
|
||||
.behaviour_mut()
|
||||
.user_protocol_mut()
|
||||
.announce_block(hash, data),
|
||||
ServiceToWorkerMsg::GetValue(key) =>
|
||||
self.network_service.behaviour_mut().get_value(key),
|
||||
ServiceToWorkerMsg::PutValue(key, value) =>
|
||||
@@ -1505,14 +1343,11 @@ where
|
||||
.behaviour_mut()
|
||||
.user_protocol_mut()
|
||||
.disconnect_peer(&who, protocol_name),
|
||||
ServiceToWorkerMsg::NewBestBlockImported(hash, number) => self
|
||||
ServiceToWorkerMsg::SetNotificationHandshake(protocol, handshake) => self
|
||||
.network_service
|
||||
.behaviour_mut()
|
||||
.user_protocol_mut()
|
||||
.new_best_block_imported(hash, number),
|
||||
ServiceToWorkerMsg::PeersDebugInfo { pending_response } => {
|
||||
let _ = pending_response.send(self.peers_debug_info());
|
||||
},
|
||||
.set_notification_handshake(protocol, handshake),
|
||||
ServiceToWorkerMsg::ReservedPeers { pending_response } => {
|
||||
let _ =
|
||||
pending_response.send(self.reserved_peers().map(ToOwned::to_owned).collect());
|
||||
@@ -1523,7 +1358,7 @@ where
|
||||
/// Process the next event coming from `Swarm`.
|
||||
fn handle_swarm_event(
|
||||
&mut self,
|
||||
event: SwarmEvent<BehaviourOut, ConnectionHandlerErr<Behaviour<B, Client>>>,
|
||||
event: SwarmEvent<BehaviourOut, ConnectionHandlerErr<Behaviour<B>>>,
|
||||
) {
|
||||
match event {
|
||||
SwarmEvent::Behaviour(BehaviourOut::InboundRequest { protocol, result, .. }) => {
|
||||
@@ -1642,6 +1477,7 @@ where
|
||||
negotiated_fallback,
|
||||
notifications_sink,
|
||||
role,
|
||||
received_handshake,
|
||||
}) => {
|
||||
if let Some(metrics) = self.metrics.as_ref() {
|
||||
metrics
|
||||
@@ -1660,6 +1496,7 @@ where
|
||||
protocol,
|
||||
negotiated_fallback,
|
||||
role,
|
||||
received_handshake,
|
||||
});
|
||||
},
|
||||
SwarmEvent::Behaviour(BehaviourOut::NotificationStreamReplaced {
|
||||
@@ -1725,12 +1562,6 @@ where
|
||||
}
|
||||
self.event_streams.send(Event::NotificationsReceived { remote, messages });
|
||||
},
|
||||
SwarmEvent::Behaviour(BehaviourOut::SyncConnected(remote)) => {
|
||||
self.event_streams.send(Event::SyncConnected { remote });
|
||||
},
|
||||
SwarmEvent::Behaviour(BehaviourOut::SyncDisconnected(remote)) => {
|
||||
self.event_streams.send(Event::SyncDisconnected { remote });
|
||||
},
|
||||
SwarmEvent::Behaviour(BehaviourOut::Dht(event, duration)) => {
|
||||
if let Some(metrics) = self.metrics.as_ref() {
|
||||
let query_type = match event {
|
||||
@@ -1925,11 +1756,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, H, Client> Unpin for NetworkWorker<B, H, Client>
|
||||
impl<B, H> Unpin for NetworkWorker<B, H>
|
||||
where
|
||||
B: BlockT + 'static,
|
||||
H: ExHashT,
|
||||
Client: HeaderBackend<B> + 'static,
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ use prometheus_endpoint::{
|
||||
use std::{
|
||||
str,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
@@ -34,7 +34,6 @@ pub use prometheus_endpoint::{Histogram, HistogramVec};
|
||||
/// Registers all networking metrics with the given registry.
|
||||
pub fn register(registry: &Registry, sources: MetricSources) -> Result<Metrics, PrometheusError> {
|
||||
BandwidthCounters::register(registry, sources.bandwidth)?;
|
||||
MajorSyncingGauge::register(registry, sources.major_syncing)?;
|
||||
NumConnectedGauge::register(registry, sources.connected_peers)?;
|
||||
Metrics::register(registry)
|
||||
}
|
||||
@@ -42,7 +41,6 @@ pub fn register(registry: &Registry, sources: MetricSources) -> Result<Metrics,
|
||||
/// Predefined metric sources that are fed directly into prometheus.
|
||||
pub struct MetricSources {
|
||||
pub bandwidth: Arc<BandwidthSinks>,
|
||||
pub major_syncing: Arc<AtomicBool>,
|
||||
pub connected_peers: Arc<AtomicUsize>,
|
||||
}
|
||||
|
||||
@@ -266,37 +264,6 @@ impl MetricSource for BandwidthCounters {
|
||||
}
|
||||
}
|
||||
|
||||
/// The "major syncing" metric.
|
||||
#[derive(Clone)]
|
||||
pub struct MajorSyncingGauge(Arc<AtomicBool>);
|
||||
|
||||
impl MajorSyncingGauge {
|
||||
/// Registers the `MajorSyncGauge` metric whose value is
|
||||
/// obtained from the given `AtomicBool`.
|
||||
fn register(registry: &Registry, value: Arc<AtomicBool>) -> Result<(), PrometheusError> {
|
||||
prometheus::register(
|
||||
SourcedGauge::new(
|
||||
&Opts::new(
|
||||
"substrate_sub_libp2p_is_major_syncing",
|
||||
"Whether the node is performing a major sync or not.",
|
||||
),
|
||||
MajorSyncingGauge(value),
|
||||
)?,
|
||||
registry,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MetricSource for MajorSyncingGauge {
|
||||
type N = u64;
|
||||
|
||||
fn collect(&self, mut set: impl FnMut(&[&str], Self::N)) {
|
||||
set(&[], self.0.load(Ordering::Relaxed) as u64);
|
||||
}
|
||||
}
|
||||
|
||||
/// The connected peers metric.
|
||||
#[derive(Clone)]
|
||||
pub struct NumConnectedGauge(Arc<AtomicUsize>);
|
||||
|
||||
@@ -268,12 +268,6 @@ impl Metrics {
|
||||
Event::Dht(_) => {
|
||||
self.events_total.with_label_values(&["dht", "sent", name]).inc();
|
||||
},
|
||||
Event::SyncConnected { .. } => {
|
||||
self.events_total.with_label_values(&["sync-connected", "sent", name]).inc();
|
||||
},
|
||||
Event::SyncDisconnected { .. } => {
|
||||
self.events_total.with_label_values(&["sync-disconnected", "sent", name]).inc();
|
||||
},
|
||||
Event::NotificationStreamOpened { protocol, .. } => {
|
||||
format_label("notif-open-", protocol, |protocol_label| {
|
||||
self.events_total.with_label_values(&[protocol_label, "sent", name]).inc();
|
||||
@@ -301,14 +295,6 @@ impl Metrics {
|
||||
Event::Dht(_) => {
|
||||
self.events_total.with_label_values(&["dht", "received", name]).inc();
|
||||
},
|
||||
Event::SyncConnected { .. } => {
|
||||
self.events_total.with_label_values(&["sync-connected", "received", name]).inc();
|
||||
},
|
||||
Event::SyncDisconnected { .. } => {
|
||||
self.events_total
|
||||
.with_label_values(&["sync-disconnected", "received", name])
|
||||
.inc();
|
||||
},
|
||||
Event::NotificationStreamOpened { protocol, .. } => {
|
||||
format_label("notif-open-", protocol, |protocol_label| {
|
||||
self.events_total.with_label_values(&[protocol_label, "received", name]).inc();
|
||||
|
||||
@@ -1,420 +0,0 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 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/>.
|
||||
|
||||
use crate::{
|
||||
config,
|
||||
service::tests::{TestNetworkBuilder, BLOCK_ANNOUNCE_PROTO_NAME},
|
||||
};
|
||||
|
||||
use futures::prelude::*;
|
||||
use libp2p::PeerId;
|
||||
use sc_block_builder::BlockBuilderProvider;
|
||||
use sc_client_api::HeaderBackend;
|
||||
use sc_consensus::JustificationSyncLink;
|
||||
use sc_network_common::{
|
||||
config::{MultiaddrWithPeerId, ProtocolId, SetConfig},
|
||||
protocol::{event::Event, role::Roles, ProtocolName},
|
||||
service::NetworkSyncForkRequest,
|
||||
sync::{SyncState, SyncStatus},
|
||||
};
|
||||
use sc_network_sync::{mock::MockChainSync, service::mock::MockChainSyncInterface, ChainSync};
|
||||
use sp_core::H256;
|
||||
use sp_runtime::traits::{Block as BlockT, Header as _};
|
||||
use std::{
|
||||
sync::{Arc, RwLock},
|
||||
task::Poll,
|
||||
time::Duration,
|
||||
};
|
||||
use substrate_test_runtime_client::{TestClientBuilder, TestClientBuilderExt as _};
|
||||
|
||||
fn set_default_expecations_no_peers(
|
||||
chain_sync: &mut MockChainSync<substrate_test_runtime_client::runtime::Block>,
|
||||
) {
|
||||
chain_sync.expect_poll().returning(|_| Poll::Pending);
|
||||
chain_sync.expect_status().returning(|| SyncStatus {
|
||||
state: SyncState::Idle,
|
||||
best_seen_block: None,
|
||||
num_peers: 0u32,
|
||||
queued_blocks: 0u32,
|
||||
state_sync: None,
|
||||
warp_sync: None,
|
||||
});
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn normal_network_poll_no_peers() {
|
||||
// build `ChainSync` and set default expectations for it
|
||||
let mut chain_sync =
|
||||
Box::new(MockChainSync::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
set_default_expecations_no_peers(&mut chain_sync);
|
||||
|
||||
// build `ChainSyncInterface` provider and set no expecations for it (i.e., it cannot be
|
||||
// called)
|
||||
let chain_sync_service =
|
||||
Box::new(MockChainSyncInterface::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
|
||||
let mut network = TestNetworkBuilder::new()
|
||||
.with_chain_sync((chain_sync, chain_sync_service))
|
||||
.build();
|
||||
|
||||
// perform one action on network
|
||||
let _ = network.network().next_action().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn request_justification() {
|
||||
let hash = H256::random();
|
||||
let number = 1337u64;
|
||||
|
||||
// build `ChainSyncInterface` provider and and expect
|
||||
// `JustificationSyncLink::request_justification() to be called once
|
||||
let mut chain_sync_service =
|
||||
Box::new(MockChainSyncInterface::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
|
||||
chain_sync_service
|
||||
.expect_justification_sync_link_request_justification()
|
||||
.withf(move |in_hash, in_number| &hash == in_hash && &number == in_number)
|
||||
.once()
|
||||
.returning(|_, _| ());
|
||||
|
||||
// build `ChainSync` and set default expecations for it
|
||||
let mut chain_sync = MockChainSync::<substrate_test_runtime_client::runtime::Block>::new();
|
||||
|
||||
set_default_expecations_no_peers(&mut chain_sync);
|
||||
let mut network = TestNetworkBuilder::new()
|
||||
.with_chain_sync((Box::new(chain_sync), chain_sync_service))
|
||||
.build();
|
||||
|
||||
// send "request justifiction" message and poll the network
|
||||
network.service().request_justification(&hash, number);
|
||||
|
||||
// perform one action on network
|
||||
let _ = network.network().next_action().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn clear_justification_requests() {
|
||||
// build `ChainSyncInterface` provider and expect
|
||||
// `JustificationSyncLink::clear_justification_requests()` to be called
|
||||
let mut chain_sync_service =
|
||||
Box::new(MockChainSyncInterface::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
|
||||
chain_sync_service
|
||||
.expect_justification_sync_link_clear_justification_requests()
|
||||
.once()
|
||||
.returning(|| ());
|
||||
|
||||
// build `ChainSync` and set default expecations for it
|
||||
let mut chain_sync =
|
||||
Box::new(MockChainSync::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
|
||||
set_default_expecations_no_peers(&mut chain_sync);
|
||||
let mut network = TestNetworkBuilder::new()
|
||||
.with_chain_sync((chain_sync, chain_sync_service))
|
||||
.build();
|
||||
|
||||
// send "request justifiction" message and poll the network
|
||||
network.service().clear_justification_requests();
|
||||
|
||||
// perform one action on network
|
||||
let _ = network.network().next_action().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn set_sync_fork_request() {
|
||||
// build `ChainSync` and set default expectations for it
|
||||
let mut chain_sync =
|
||||
Box::new(MockChainSync::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
set_default_expecations_no_peers(&mut chain_sync);
|
||||
|
||||
// build `ChainSyncInterface` provider and verify that the `set_sync_fork_request()`
|
||||
// call is delegated to `ChainSyncInterface` (which eventually forwards it to `ChainSync`)
|
||||
let mut chain_sync_service =
|
||||
MockChainSyncInterface::<substrate_test_runtime_client::runtime::Block>::new();
|
||||
|
||||
let hash = H256::random();
|
||||
let number = 1337u64;
|
||||
let peers = (0..3).map(|_| PeerId::random()).collect::<Vec<_>>();
|
||||
let copy_peers = peers.clone();
|
||||
|
||||
chain_sync_service
|
||||
.expect_set_sync_fork_request()
|
||||
.withf(move |in_peers, in_hash, in_number| {
|
||||
&peers == in_peers && &hash == in_hash && &number == in_number
|
||||
})
|
||||
.once()
|
||||
.returning(|_, _, _| ());
|
||||
|
||||
let mut network = TestNetworkBuilder::new()
|
||||
.with_chain_sync((chain_sync, Box::new(chain_sync_service)))
|
||||
.build();
|
||||
|
||||
// send "set sync fork request" message and poll the network
|
||||
network.service().set_sync_fork_request(copy_peers, hash, number);
|
||||
|
||||
// perform one action on network
|
||||
let _ = network.network().next_action().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn on_block_finalized() {
|
||||
let client = Arc::new(TestClientBuilder::with_default_backend().build_with_longest_chain().0);
|
||||
// build `ChainSyncInterface` provider and set no expecations for it (i.e., it cannot be
|
||||
// called)
|
||||
let chain_sync_service =
|
||||
Box::new(MockChainSyncInterface::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
|
||||
// build `ChainSync` and verify that call to `on_block_finalized()` is made
|
||||
let mut chain_sync =
|
||||
Box::new(MockChainSync::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
|
||||
let at = client.header(client.info().best_hash).unwrap().unwrap().hash();
|
||||
let block = client
|
||||
.new_block_at(at, Default::default(), false)
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block;
|
||||
let header = block.header.clone();
|
||||
let block_number = *header.number();
|
||||
let hash = block.hash();
|
||||
|
||||
chain_sync
|
||||
.expect_on_block_finalized()
|
||||
.withf(move |in_hash, in_number| &hash == in_hash && &block_number == in_number)
|
||||
.once()
|
||||
.returning(|_, _| ());
|
||||
|
||||
set_default_expecations_no_peers(&mut chain_sync);
|
||||
let mut network = TestNetworkBuilder::new()
|
||||
.with_client(client)
|
||||
.with_chain_sync((chain_sync, chain_sync_service))
|
||||
.build();
|
||||
|
||||
// send "set sync fork request" message and poll the network
|
||||
network.network().on_block_finalized(hash, header);
|
||||
|
||||
// perform one action on network
|
||||
let _ = network.network().next_action().await;
|
||||
}
|
||||
|
||||
// report from mock import queue that importing a justification was not successful
|
||||
// and verify that connection to the peer is closed
|
||||
#[tokio::test]
|
||||
async fn invalid_justification_imported() {
|
||||
struct DummyImportQueueHandle;
|
||||
|
||||
impl
|
||||
sc_consensus::import_queue::ImportQueueService<
|
||||
substrate_test_runtime_client::runtime::Block,
|
||||
> for DummyImportQueueHandle
|
||||
{
|
||||
fn import_blocks(
|
||||
&mut self,
|
||||
_origin: sp_consensus::BlockOrigin,
|
||||
_blocks: Vec<
|
||||
sc_consensus::IncomingBlock<substrate_test_runtime_client::runtime::Block>,
|
||||
>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn import_justifications(
|
||||
&mut self,
|
||||
_who: sc_consensus::import_queue::RuntimeOrigin,
|
||||
_hash: substrate_test_runtime_client::runtime::Hash,
|
||||
_number: sp_runtime::traits::NumberFor<substrate_test_runtime_client::runtime::Block>,
|
||||
_justifications: sp_runtime::Justifications,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyImportQueue(
|
||||
Arc<
|
||||
RwLock<
|
||||
Option<(
|
||||
PeerId,
|
||||
substrate_test_runtime_client::runtime::Hash,
|
||||
sp_runtime::traits::NumberFor<substrate_test_runtime_client::runtime::Block>,
|
||||
)>,
|
||||
>,
|
||||
>,
|
||||
DummyImportQueueHandle,
|
||||
);
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl sc_consensus::ImportQueue<substrate_test_runtime_client::runtime::Block> for DummyImportQueue {
|
||||
fn poll_actions(
|
||||
&mut self,
|
||||
_cx: &mut futures::task::Context,
|
||||
link: &mut dyn sc_consensus::Link<substrate_test_runtime_client::runtime::Block>,
|
||||
) {
|
||||
if let Some((peer, hash, number)) = *self.0.read().unwrap() {
|
||||
link.justification_imported(peer, &hash, number, false);
|
||||
}
|
||||
}
|
||||
|
||||
fn service(
|
||||
&self,
|
||||
) -> Box<
|
||||
dyn sc_consensus::import_queue::ImportQueueService<
|
||||
substrate_test_runtime_client::runtime::Block,
|
||||
>,
|
||||
> {
|
||||
Box::new(DummyImportQueueHandle {})
|
||||
}
|
||||
|
||||
fn service_ref(
|
||||
&mut self,
|
||||
) -> &mut dyn sc_consensus::import_queue::ImportQueueService<
|
||||
substrate_test_runtime_client::runtime::Block,
|
||||
> {
|
||||
&mut self.1
|
||||
}
|
||||
|
||||
async fn run(
|
||||
self,
|
||||
_link: Box<dyn sc_consensus::Link<substrate_test_runtime_client::runtime::Block>>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
let justification_info = Arc::new(RwLock::new(None));
|
||||
let listen_addr = config::build_multiaddr![Memory(rand::random::<u64>())];
|
||||
|
||||
let (service1, mut event_stream1) = TestNetworkBuilder::new()
|
||||
.with_import_queue(Box::new(DummyImportQueue(
|
||||
justification_info.clone(),
|
||||
DummyImportQueueHandle {},
|
||||
)))
|
||||
.with_listen_addresses(vec![listen_addr.clone()])
|
||||
.build()
|
||||
.start_network();
|
||||
|
||||
let (service2, mut event_stream2) = TestNetworkBuilder::new()
|
||||
.with_set_config(SetConfig {
|
||||
reserved_nodes: vec![MultiaddrWithPeerId {
|
||||
multiaddr: listen_addr,
|
||||
peer_id: service1.local_peer_id,
|
||||
}],
|
||||
..Default::default()
|
||||
})
|
||||
.build()
|
||||
.start_network();
|
||||
|
||||
async fn wait_for_events(stream: &mut (impl Stream<Item = Event> + std::marker::Unpin)) {
|
||||
let mut notif_received = false;
|
||||
let mut sync_received = false;
|
||||
while !notif_received || !sync_received {
|
||||
match stream.next().await.unwrap() {
|
||||
Event::NotificationStreamOpened { .. } => notif_received = true,
|
||||
Event::SyncConnected { .. } => sync_received = true,
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
wait_for_events(&mut event_stream1).await;
|
||||
wait_for_events(&mut event_stream2).await;
|
||||
|
||||
{
|
||||
let mut info = justification_info.write().unwrap();
|
||||
*info = Some((service2.local_peer_id, H256::random(), 1337u64));
|
||||
}
|
||||
|
||||
let wait_disconnection = async {
|
||||
while !std::matches!(event_stream1.next().await, Some(Event::SyncDisconnected { .. })) {}
|
||||
};
|
||||
|
||||
if tokio::time::timeout(Duration::from_secs(5), wait_disconnection).await.is_err() {
|
||||
panic!("did not receive disconnection event in time");
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn disconnect_peer_using_chain_sync_handle() {
|
||||
let client = Arc::new(TestClientBuilder::with_default_backend().build_with_longest_chain().0);
|
||||
let listen_addr = config::build_multiaddr![Memory(rand::random::<u64>())];
|
||||
|
||||
let import_queue = Box::new(sc_consensus::import_queue::mock::MockImportQueueHandle::new());
|
||||
let (chain_sync_network_provider, chain_sync_network_handle) =
|
||||
sc_network_sync::service::network::NetworkServiceProvider::new();
|
||||
let handle_clone = chain_sync_network_handle.clone();
|
||||
|
||||
let (chain_sync, chain_sync_service, _) = ChainSync::new(
|
||||
sc_network_common::sync::SyncMode::Full,
|
||||
client.clone(),
|
||||
ProtocolId::from("test-protocol-name"),
|
||||
&Some(String::from("test-fork-id")),
|
||||
Roles::from(&config::Role::Full),
|
||||
Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator),
|
||||
1u32,
|
||||
None,
|
||||
None,
|
||||
chain_sync_network_handle.clone(),
|
||||
import_queue,
|
||||
ProtocolName::from("block-request"),
|
||||
ProtocolName::from("state-request"),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (node1, mut event_stream1) = TestNetworkBuilder::new()
|
||||
.with_listen_addresses(vec![listen_addr.clone()])
|
||||
.with_chain_sync((Box::new(chain_sync), Box::new(chain_sync_service)))
|
||||
.with_chain_sync_network((chain_sync_network_provider, chain_sync_network_handle))
|
||||
.with_client(client.clone())
|
||||
.build()
|
||||
.start_network();
|
||||
|
||||
let (node2, mut event_stream2) = TestNetworkBuilder::new()
|
||||
.with_set_config(SetConfig {
|
||||
reserved_nodes: vec![MultiaddrWithPeerId {
|
||||
multiaddr: listen_addr,
|
||||
peer_id: node1.local_peer_id,
|
||||
}],
|
||||
..Default::default()
|
||||
})
|
||||
.with_client(client.clone())
|
||||
.build()
|
||||
.start_network();
|
||||
|
||||
async fn wait_for_events(stream: &mut (impl Stream<Item = Event> + std::marker::Unpin)) {
|
||||
let mut notif_received = false;
|
||||
let mut sync_received = false;
|
||||
while !notif_received || !sync_received {
|
||||
match stream.next().await.unwrap() {
|
||||
Event::NotificationStreamOpened { .. } => notif_received = true,
|
||||
Event::SyncConnected { .. } => sync_received = true,
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
wait_for_events(&mut event_stream1).await;
|
||||
wait_for_events(&mut event_stream2).await;
|
||||
|
||||
handle_clone.disconnect_peer(node2.local_peer_id, BLOCK_ANNOUNCE_PROTO_NAME.into());
|
||||
|
||||
let wait_disconnection = async {
|
||||
while !std::matches!(event_stream1.next().await, Some(Event::SyncDisconnected { .. })) {}
|
||||
};
|
||||
|
||||
if tokio::time::timeout(Duration::from_secs(5), wait_disconnection).await.is_err() {
|
||||
panic!("did not receive disconnection event in time");
|
||||
}
|
||||
}
|
||||
@@ -16,44 +16,36 @@
|
||||
// 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, ChainSyncInterface, NetworkService, NetworkWorker};
|
||||
use crate::{config, NetworkService, NetworkWorker};
|
||||
|
||||
use futures::prelude::*;
|
||||
use libp2p::Multiaddr;
|
||||
use sc_client_api::{BlockBackend, HeaderBackend};
|
||||
use sc_consensus::{ImportQueue, Link};
|
||||
use sc_network_common::{
|
||||
config::{
|
||||
NonDefaultSetConfig, NonReservedPeerMode, NotificationHandshake, ProtocolId, SetConfig,
|
||||
TransportConfig,
|
||||
},
|
||||
config::{NonDefaultSetConfig, ProtocolId, SetConfig, TransportConfig},
|
||||
protocol::{event::Event, role::Roles},
|
||||
service::NetworkEventStream,
|
||||
sync::{message::BlockAnnouncesHandshake, ChainSync as ChainSyncT},
|
||||
};
|
||||
use sc_network_light::light_client_requests::handler::LightClientRequestHandler;
|
||||
use sc_network_sync::{
|
||||
block_request_handler::BlockRequestHandler,
|
||||
engine::SyncingEngine,
|
||||
service::network::{NetworkServiceHandle, NetworkServiceProvider},
|
||||
state_request_handler::StateRequestHandler,
|
||||
ChainSync,
|
||||
};
|
||||
use sp_runtime::traits::{Block as BlockT, Header as _, Zero};
|
||||
use sp_runtime::traits::{Block as BlockT, Header as _};
|
||||
use std::sync::Arc;
|
||||
use substrate_test_runtime_client::{
|
||||
runtime::{Block as TestBlock, Hash as TestHash},
|
||||
TestClient, TestClientBuilder, TestClientBuilderExt as _,
|
||||
TestClientBuilder, TestClientBuilderExt as _,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
mod chain_sync;
|
||||
#[cfg(test)]
|
||||
mod service;
|
||||
|
||||
type TestNetworkWorker = NetworkWorker<TestBlock, TestHash, TestClient>;
|
||||
type TestNetworkWorker = NetworkWorker<TestBlock, TestHash>;
|
||||
type TestNetworkService = NetworkService<TestBlock, TestHash>;
|
||||
|
||||
const BLOCK_ANNOUNCE_PROTO_NAME: &str = "/block-announces";
|
||||
const PROTOCOL_NAME: &str = "/foo";
|
||||
|
||||
struct TestNetwork {
|
||||
@@ -65,14 +57,6 @@ impl TestNetwork {
|
||||
Self { network }
|
||||
}
|
||||
|
||||
pub fn service(&self) -> &Arc<TestNetworkService> {
|
||||
&self.network.service()
|
||||
}
|
||||
|
||||
pub fn network(&mut self) -> &mut TestNetworkWorker {
|
||||
&mut self.network
|
||||
}
|
||||
|
||||
pub fn start_network(
|
||||
self,
|
||||
) -> (Arc<TestNetworkService>, (impl Stream<Item = Event> + std::marker::Unpin)) {
|
||||
@@ -92,7 +76,6 @@ struct TestNetworkBuilder {
|
||||
client: Option<Arc<substrate_test_runtime_client::TestClient>>,
|
||||
listen_addresses: Vec<Multiaddr>,
|
||||
set_config: Option<SetConfig>,
|
||||
chain_sync: Option<(Box<dyn ChainSyncT<TestBlock>>, Box<dyn ChainSyncInterface<TestBlock>>)>,
|
||||
chain_sync_network: Option<(NetworkServiceProvider, NetworkServiceHandle)>,
|
||||
config: Option<config::NetworkConfiguration>,
|
||||
}
|
||||
@@ -105,17 +88,11 @@ impl TestNetworkBuilder {
|
||||
client: None,
|
||||
listen_addresses: Vec::new(),
|
||||
set_config: None,
|
||||
chain_sync: None,
|
||||
chain_sync_network: None,
|
||||
config: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_client(mut self, client: Arc<substrate_test_runtime_client::TestClient>) -> Self {
|
||||
self.client = Some(client);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_config(mut self, config: config::NetworkConfiguration) -> Self {
|
||||
self.config = Some(config);
|
||||
self
|
||||
@@ -131,27 +108,6 @@ impl TestNetworkBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_chain_sync(
|
||||
mut self,
|
||||
chain_sync: (Box<dyn ChainSyncT<TestBlock>>, Box<dyn ChainSyncInterface<TestBlock>>),
|
||||
) -> Self {
|
||||
self.chain_sync = Some(chain_sync);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_chain_sync_network(
|
||||
mut self,
|
||||
chain_sync_network: (NetworkServiceProvider, NetworkServiceHandle),
|
||||
) -> Self {
|
||||
self.chain_sync_network = Some(chain_sync_network);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_import_queue(mut self, import_queue: Box<dyn ImportQueue<TestBlock>>) -> Self {
|
||||
self.import_queue = Some(import_queue);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(mut self) -> TestNetwork {
|
||||
let client = self.client.as_mut().map_or(
|
||||
Arc::new(TestClientBuilder::with_default_backend().build_with_longest_chain().0),
|
||||
@@ -240,73 +196,29 @@ impl TestNetworkBuilder {
|
||||
protocol_config
|
||||
};
|
||||
|
||||
let block_announce_config = NonDefaultSetConfig {
|
||||
notifications_protocol: BLOCK_ANNOUNCE_PROTO_NAME.into(),
|
||||
fallback_names: vec![],
|
||||
max_notification_size: 1024 * 1024,
|
||||
handshake: Some(NotificationHandshake::new(BlockAnnouncesHandshake::<
|
||||
substrate_test_runtime_client::runtime::Block,
|
||||
>::build(
|
||||
Roles::from(&config::Role::Full),
|
||||
client.info().best_number,
|
||||
client.info().best_hash,
|
||||
client
|
||||
.block_hash(Zero::zero())
|
||||
.ok()
|
||||
.flatten()
|
||||
.expect("Genesis block exists; qed"),
|
||||
))),
|
||||
set_config: SetConfig {
|
||||
in_peers: 0,
|
||||
out_peers: 0,
|
||||
reserved_nodes: Vec::new(),
|
||||
non_reserved_mode: NonReservedPeerMode::Deny,
|
||||
},
|
||||
};
|
||||
|
||||
let (chain_sync_network_provider, chain_sync_network_handle) =
|
||||
self.chain_sync_network.unwrap_or(NetworkServiceProvider::new());
|
||||
|
||||
let (chain_sync, chain_sync_service) = self.chain_sync.unwrap_or({
|
||||
let (chain_sync, chain_sync_service, _) = ChainSync::new(
|
||||
match network_config.sync_mode {
|
||||
config::SyncMode::Full => sc_network_common::sync::SyncMode::Full,
|
||||
config::SyncMode::Fast { skip_proofs, storage_chain_mode } =>
|
||||
sc_network_common::sync::SyncMode::LightState {
|
||||
skip_proofs,
|
||||
storage_chain_mode,
|
||||
},
|
||||
config::SyncMode::Warp => sc_network_common::sync::SyncMode::Warp,
|
||||
},
|
||||
client.clone(),
|
||||
protocol_id.clone(),
|
||||
&fork_id,
|
||||
Roles::from(&config::Role::Full),
|
||||
Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator),
|
||||
network_config.max_parallel_downloads,
|
||||
None,
|
||||
None,
|
||||
chain_sync_network_handle,
|
||||
import_queue.service(),
|
||||
block_request_protocol_config.name.clone(),
|
||||
state_request_protocol_config.name.clone(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if let None = self.link {
|
||||
self.link = Some(Box::new(chain_sync_service.clone()));
|
||||
}
|
||||
(Box::new(chain_sync), Box::new(chain_sync_service))
|
||||
});
|
||||
let mut link = self
|
||||
.link
|
||||
.unwrap_or(Box::new(sc_network_sync::service::mock::MockChainSyncInterface::new()));
|
||||
|
||||
let (engine, chain_sync_service, block_announce_config) = SyncingEngine::new(
|
||||
Roles::from(&config::Role::Full),
|
||||
client.clone(),
|
||||
None,
|
||||
&network_config,
|
||||
protocol_id.clone(),
|
||||
&None,
|
||||
Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator),
|
||||
None,
|
||||
chain_sync_network_handle,
|
||||
import_queue.service(),
|
||||
block_request_protocol_config.name.clone(),
|
||||
state_request_protocol_config.name.clone(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let mut link = self.link.unwrap_or(Box::new(chain_sync_service.clone()));
|
||||
let worker = NetworkWorker::<
|
||||
substrate_test_runtime_client::runtime::Block,
|
||||
substrate_test_runtime_client::runtime::Hash,
|
||||
substrate_test_runtime_client::TestClient,
|
||||
>::new(config::Params {
|
||||
block_announce_config,
|
||||
role: config::Role::Full,
|
||||
@@ -317,8 +229,6 @@ impl TestNetworkBuilder {
|
||||
chain: client.clone(),
|
||||
protocol_id,
|
||||
fork_id,
|
||||
chain_sync,
|
||||
chain_sync_service,
|
||||
metrics_registry: None,
|
||||
request_response_protocol_configs: [
|
||||
block_request_protocol_config,
|
||||
@@ -343,6 +253,8 @@ impl TestNetworkBuilder {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(250)).await;
|
||||
}
|
||||
});
|
||||
let stream = worker.service().event_stream("syncing");
|
||||
tokio::spawn(engine.run(stream));
|
||||
|
||||
TestNetwork::new(worker)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ type TestNetworkService = NetworkService<
|
||||
substrate_test_runtime_client::runtime::Hash,
|
||||
>;
|
||||
|
||||
const BLOCK_ANNOUNCE_PROTO_NAME: &str = "/block-announces";
|
||||
const PROTOCOL_NAME: &str = "/foo";
|
||||
|
||||
/// Builds two nodes and their associated events stream.
|
||||
@@ -196,10 +195,6 @@ async fn notifications_state_consistent() {
|
||||
},
|
||||
|
||||
// Add new events here.
|
||||
future::Either::Left(Event::SyncConnected { .. }) => {},
|
||||
future::Either::Right(Event::SyncConnected { .. }) => {},
|
||||
future::Either::Left(Event::SyncDisconnected { .. }) => {},
|
||||
future::Either::Right(Event::SyncDisconnected { .. }) => {},
|
||||
future::Either::Left(Event::Dht(_)) => {},
|
||||
future::Either::Right(Event::Dht(_)) => {},
|
||||
};
|
||||
@@ -208,6 +203,7 @@ async fn notifications_state_consistent() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn lots_of_incoming_peers_works() {
|
||||
sp_tracing::try_init_simple();
|
||||
let listen_addr = config::build_multiaddr![Memory(rand::random::<u64>())];
|
||||
|
||||
let (main_node, _) = TestNetworkBuilder::new()
|
||||
@@ -241,6 +237,7 @@ async fn lots_of_incoming_peers_works() {
|
||||
let mut timer = futures_timer::Delay::new(Duration::from_secs(3600 * 24 * 7)).fuse();
|
||||
|
||||
let mut event_stream = event_stream.fuse();
|
||||
let mut sync_protocol_name = None;
|
||||
loop {
|
||||
futures::select! {
|
||||
_ = timer => {
|
||||
@@ -249,15 +246,21 @@ async fn lots_of_incoming_peers_works() {
|
||||
}
|
||||
ev = event_stream.next() => {
|
||||
match ev.unwrap() {
|
||||
Event::NotificationStreamOpened { remote, .. } => {
|
||||
Event::NotificationStreamOpened { protocol, remote, .. } => {
|
||||
if let None = sync_protocol_name {
|
||||
sync_protocol_name = Some(protocol.clone());
|
||||
}
|
||||
|
||||
assert_eq!(remote, main_node_peer_id);
|
||||
// Test succeeds after 5 seconds. This timer is here in order to
|
||||
// detect a potential problem after opening.
|
||||
timer = futures_timer::Delay::new(Duration::from_secs(5)).fuse();
|
||||
}
|
||||
Event::NotificationStreamClosed { .. } => {
|
||||
// Test failed.
|
||||
panic!();
|
||||
Event::NotificationStreamClosed { protocol, .. } => {
|
||||
if Some(protocol) != sync_protocol_name {
|
||||
// Test failed.
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -282,10 +285,19 @@ async fn notifications_back_pressure() {
|
||||
|
||||
let receiver = tokio::spawn(async move {
|
||||
let mut received_notifications = 0;
|
||||
let mut sync_protocol_name = None;
|
||||
|
||||
while received_notifications < TOTAL_NOTIFS {
|
||||
match events_stream2.next().await.unwrap() {
|
||||
Event::NotificationStreamClosed { .. } => panic!(),
|
||||
Event::NotificationStreamOpened { protocol, .. } =>
|
||||
if let None = sync_protocol_name {
|
||||
sync_protocol_name = Some(protocol);
|
||||
},
|
||||
Event::NotificationStreamClosed { protocol, .. } => {
|
||||
if Some(&protocol) != sync_protocol_name.as_ref() {
|
||||
panic!()
|
||||
}
|
||||
},
|
||||
Event::NotificationsReceived { messages, .. } =>
|
||||
for message in messages {
|
||||
assert_eq!(message.0, PROTOCOL_NAME.into());
|
||||
@@ -387,42 +399,6 @@ async fn fallback_name_working() {
|
||||
receiver.await.unwrap();
|
||||
}
|
||||
|
||||
// Disconnect peer by calling `Protocol::disconnect_peer()` with the supplied block announcement
|
||||
// protocol name and verify that `SyncDisconnected` event is emitted
|
||||
#[tokio::test]
|
||||
async fn disconnect_sync_peer_using_block_announcement_protocol_name() {
|
||||
let (node1, mut events_stream1, node2, mut events_stream2) = build_nodes_one_proto();
|
||||
|
||||
async fn wait_for_events(stream: &mut (impl Stream<Item = Event> + std::marker::Unpin)) {
|
||||
let mut notif_received = false;
|
||||
let mut sync_received = false;
|
||||
|
||||
while !notif_received || !sync_received {
|
||||
match stream.next().await.unwrap() {
|
||||
Event::NotificationStreamOpened { .. } => notif_received = true,
|
||||
Event::SyncConnected { .. } => sync_received = true,
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
wait_for_events(&mut events_stream1).await;
|
||||
wait_for_events(&mut events_stream2).await;
|
||||
|
||||
// disconnect peer using `PROTOCOL_NAME`, verify `NotificationStreamClosed` event is emitted
|
||||
node2.disconnect_peer(node1.local_peer_id(), PROTOCOL_NAME.into());
|
||||
assert!(std::matches!(
|
||||
events_stream2.next().await,
|
||||
Some(Event::NotificationStreamClosed { .. })
|
||||
));
|
||||
let _ = events_stream2.next().await; // ignore the reopen event
|
||||
|
||||
// now disconnect using `BLOCK_ANNOUNCE_PROTO_NAME`, verify that `SyncDisconnected` is
|
||||
// emitted
|
||||
node2.disconnect_peer(node1.local_peer_id(), BLOCK_ANNOUNCE_PROTO_NAME.into());
|
||||
assert!(std::matches!(events_stream2.next().await, Some(Event::SyncDisconnected { .. })));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[should_panic(expected = "don't match the transport")]
|
||||
async fn ensure_listen_addresses_consistent_with_transport_memory() {
|
||||
|
||||
@@ -20,6 +20,7 @@ array-bytes = "4.1"
|
||||
async-trait = "0.1.58"
|
||||
codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] }
|
||||
futures = "0.3.21"
|
||||
futures-timer = "3.0.2"
|
||||
libp2p = "0.50.0"
|
||||
log = "0.4.17"
|
||||
lru = "0.8.1"
|
||||
|
||||
@@ -0,0 +1,924 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2017-2023 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/>.
|
||||
|
||||
//! `SyncingEngine` is the actor responsible for syncing Substrate chain
|
||||
//! to tip and keep the blockchain up to date with network updates.
|
||||
|
||||
use crate::{
|
||||
service::{self, chain_sync::ToServiceCommand},
|
||||
ChainSync, ClientError, SyncingService,
|
||||
};
|
||||
|
||||
use futures::{FutureExt, Stream, StreamExt};
|
||||
use libp2p::PeerId;
|
||||
use lru::LruCache;
|
||||
use prometheus_endpoint::{
|
||||
register, Gauge, GaugeVec, MetricSource, Opts, PrometheusError, Registry, SourcedGauge, U64,
|
||||
};
|
||||
|
||||
use codec::{Decode, DecodeAll, Encode};
|
||||
use futures_timer::Delay;
|
||||
use sc_client_api::{BlockBackend, HeaderBackend, ProofProvider};
|
||||
use sc_consensus::import_queue::ImportQueueService;
|
||||
use sc_network_common::{
|
||||
config::{
|
||||
NetworkConfiguration, NonDefaultSetConfig, ProtocolId, SyncMode as SyncOperationMode,
|
||||
},
|
||||
protocol::{event::Event, role::Roles, ProtocolName},
|
||||
sync::{
|
||||
message::{
|
||||
generic::{BlockData, BlockResponse},
|
||||
BlockAnnounce, BlockAnnouncesHandshake, BlockState,
|
||||
},
|
||||
warp::WarpSyncParams,
|
||||
BadPeer, ChainSync as ChainSyncT, ExtendedPeerInfo, PollBlockAnnounceValidation, SyncEvent,
|
||||
SyncMode,
|
||||
},
|
||||
utils::LruHashSet,
|
||||
};
|
||||
use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender};
|
||||
use sp_blockchain::HeaderMetadata;
|
||||
use sp_consensus::block_validation::BlockAnnounceValidator;
|
||||
use sp_runtime::{
|
||||
traits::{Block as BlockT, CheckedSub, Header, NumberFor, Zero},
|
||||
SaturatedConversion,
|
||||
};
|
||||
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
num::NonZeroUsize,
|
||||
pin::Pin,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
task::Poll,
|
||||
};
|
||||
|
||||
/// Interval at which we perform time based maintenance
|
||||
const TICK_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(1100);
|
||||
|
||||
/// When light node connects to the full node and the full node is behind light node
|
||||
/// for at least `LIGHT_MAXIMAL_BLOCKS_DIFFERENCE` blocks, we consider it not useful
|
||||
/// and disconnect to free connection slot.
|
||||
const LIGHT_MAXIMAL_BLOCKS_DIFFERENCE: u64 = 8192;
|
||||
|
||||
/// Maximum number of known block hashes to keep for a peer.
|
||||
const MAX_KNOWN_BLOCKS: usize = 1024; // ~32kb per peer + LruHashSet overhead
|
||||
|
||||
mod rep {
|
||||
use sc_peerset::ReputationChange as Rep;
|
||||
/// Reputation change when we are a light client and a peer is behind us.
|
||||
pub const PEER_BEHIND_US_LIGHT: Rep = Rep::new(-(1 << 8), "Useless for a light peer");
|
||||
/// We received a message that failed to decode.
|
||||
pub const BAD_MESSAGE: Rep = Rep::new(-(1 << 12), "Bad message");
|
||||
/// Peer has different genesis.
|
||||
pub const GENESIS_MISMATCH: Rep = Rep::new_fatal("Genesis mismatch");
|
||||
/// Peer role does not match (e.g. light peer connecting to another light peer).
|
||||
pub const BAD_ROLE: Rep = Rep::new_fatal("Unsupported role");
|
||||
/// Peer send us a block announcement that failed at validation.
|
||||
pub const BAD_BLOCK_ANNOUNCEMENT: Rep = Rep::new(-(1 << 12), "Bad block announcement");
|
||||
}
|
||||
|
||||
struct Metrics {
|
||||
peers: Gauge<U64>,
|
||||
queued_blocks: Gauge<U64>,
|
||||
fork_targets: Gauge<U64>,
|
||||
justifications: GaugeVec<U64>,
|
||||
}
|
||||
|
||||
impl Metrics {
|
||||
fn register(r: &Registry, major_syncing: Arc<AtomicBool>) -> Result<Self, PrometheusError> {
|
||||
let _ = MajorSyncingGauge::register(r, major_syncing)?;
|
||||
Ok(Self {
|
||||
peers: {
|
||||
let g = Gauge::new("substrate_sync_peers", "Number of peers we sync with")?;
|
||||
register(g, r)?
|
||||
},
|
||||
queued_blocks: {
|
||||
let g =
|
||||
Gauge::new("substrate_sync_queued_blocks", "Number of blocks in import queue")?;
|
||||
register(g, r)?
|
||||
},
|
||||
fork_targets: {
|
||||
let g = Gauge::new("substrate_sync_fork_targets", "Number of fork sync targets")?;
|
||||
register(g, r)?
|
||||
},
|
||||
justifications: {
|
||||
let g = GaugeVec::new(
|
||||
Opts::new(
|
||||
"substrate_sync_extra_justifications",
|
||||
"Number of extra justifications requests",
|
||||
),
|
||||
&["status"],
|
||||
)?;
|
||||
register(g, r)?
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The "major syncing" metric.
|
||||
#[derive(Clone)]
|
||||
pub struct MajorSyncingGauge(Arc<AtomicBool>);
|
||||
|
||||
impl MajorSyncingGauge {
|
||||
/// Registers the [`MajorSyncGauge`] metric whose value is
|
||||
/// obtained from the given `AtomicBool`.
|
||||
fn register(registry: &Registry, value: Arc<AtomicBool>) -> Result<(), PrometheusError> {
|
||||
prometheus_endpoint::register(
|
||||
SourcedGauge::new(
|
||||
&Opts::new(
|
||||
"substrate_sub_libp2p_is_major_syncing",
|
||||
"Whether the node is performing a major sync or not.",
|
||||
),
|
||||
MajorSyncingGauge(value),
|
||||
)?,
|
||||
registry,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MetricSource for MajorSyncingGauge {
|
||||
type N = u64;
|
||||
|
||||
fn collect(&self, mut set: impl FnMut(&[&str], Self::N)) {
|
||||
set(&[], self.0.load(Ordering::Relaxed) as u64);
|
||||
}
|
||||
}
|
||||
|
||||
/// Peer information
|
||||
#[derive(Debug)]
|
||||
pub struct Peer<B: BlockT> {
|
||||
pub info: ExtendedPeerInfo<B>,
|
||||
/// Holds a set of blocks known to this peer.
|
||||
pub known_blocks: LruHashSet<B::Hash>,
|
||||
}
|
||||
|
||||
pub struct SyncingEngine<B: BlockT, Client> {
|
||||
/// State machine that handles the list of in-progress requests. Only full node peers are
|
||||
/// registered.
|
||||
chain_sync: ChainSync<B, Client>,
|
||||
|
||||
/// Blockchain client.
|
||||
client: Arc<Client>,
|
||||
|
||||
/// Number of peers we're connected to.
|
||||
num_connected: Arc<AtomicUsize>,
|
||||
|
||||
/// Are we actively catching up with the chain?
|
||||
is_major_syncing: Arc<AtomicBool>,
|
||||
|
||||
/// Network service.
|
||||
network_service: service::network::NetworkServiceHandle,
|
||||
|
||||
/// Channel for receiving service commands
|
||||
service_rx: TracingUnboundedReceiver<ToServiceCommand<B>>,
|
||||
|
||||
/// Assigned roles.
|
||||
roles: Roles,
|
||||
|
||||
/// Genesis hash.
|
||||
genesis_hash: B::Hash,
|
||||
|
||||
/// Set of channels for other protocols that have subscribed to syncing events.
|
||||
event_streams: Vec<TracingUnboundedSender<SyncEvent>>,
|
||||
|
||||
/// Interval at which we call `tick`.
|
||||
tick_timeout: Delay,
|
||||
|
||||
/// All connected peers. Contains both full and light node peers.
|
||||
peers: HashMap<PeerId, Peer<B>>,
|
||||
|
||||
/// List of nodes for which we perform additional logging because they are important for the
|
||||
/// user.
|
||||
important_peers: HashSet<PeerId>,
|
||||
|
||||
/// Actual list of connected no-slot nodes.
|
||||
default_peers_set_no_slot_connected_peers: HashSet<PeerId>,
|
||||
|
||||
/// List of nodes that should never occupy peer slots.
|
||||
default_peers_set_no_slot_peers: HashSet<PeerId>,
|
||||
|
||||
/// Value that was passed as part of the configuration. Used to cap the number of full
|
||||
/// nodes.
|
||||
default_peers_set_num_full: usize,
|
||||
|
||||
/// Number of slots to allocate to light nodes.
|
||||
default_peers_set_num_light: usize,
|
||||
|
||||
/// A cache for the data that was associated to a block announcement.
|
||||
block_announce_data_cache: LruCache<B::Hash, Vec<u8>>,
|
||||
|
||||
/// The `PeerId`'s of all boot nodes.
|
||||
boot_node_ids: HashSet<PeerId>,
|
||||
|
||||
/// Protocol name used for block announcements
|
||||
block_announce_protocol_name: ProtocolName,
|
||||
|
||||
/// Prometheus metrics.
|
||||
metrics: Option<Metrics>,
|
||||
}
|
||||
|
||||
impl<B: BlockT, Client> SyncingEngine<B, Client>
|
||||
where
|
||||
B: BlockT,
|
||||
Client: HeaderBackend<B>
|
||||
+ BlockBackend<B>
|
||||
+ HeaderMetadata<B, Error = sp_blockchain::Error>
|
||||
+ ProofProvider<B>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
{
|
||||
pub fn new(
|
||||
roles: Roles,
|
||||
client: Arc<Client>,
|
||||
metrics_registry: Option<&Registry>,
|
||||
network_config: &NetworkConfiguration,
|
||||
protocol_id: ProtocolId,
|
||||
fork_id: &Option<String>,
|
||||
block_announce_validator: Box<dyn BlockAnnounceValidator<B> + Send>,
|
||||
warp_sync_params: Option<WarpSyncParams<B>>,
|
||||
network_service: service::network::NetworkServiceHandle,
|
||||
import_queue: Box<dyn ImportQueueService<B>>,
|
||||
block_request_protocol_name: ProtocolName,
|
||||
state_request_protocol_name: ProtocolName,
|
||||
warp_sync_protocol_name: Option<ProtocolName>,
|
||||
) -> Result<(Self, SyncingService<B>, NonDefaultSetConfig), ClientError> {
|
||||
let mode = match network_config.sync_mode {
|
||||
SyncOperationMode::Full => SyncMode::Full,
|
||||
SyncOperationMode::Fast { skip_proofs, storage_chain_mode } =>
|
||||
SyncMode::LightState { skip_proofs, storage_chain_mode },
|
||||
SyncOperationMode::Warp => SyncMode::Warp,
|
||||
};
|
||||
let max_parallel_downloads = network_config.max_parallel_downloads;
|
||||
let cache_capacity = NonZeroUsize::new(
|
||||
(network_config.default_peers_set.in_peers as usize +
|
||||
network_config.default_peers_set.out_peers as usize)
|
||||
.max(1),
|
||||
)
|
||||
.expect("cache capacity is not zero");
|
||||
let important_peers = {
|
||||
let mut imp_p = HashSet::new();
|
||||
for reserved in &network_config.default_peers_set.reserved_nodes {
|
||||
imp_p.insert(reserved.peer_id);
|
||||
}
|
||||
for reserved in network_config
|
||||
.extra_sets
|
||||
.iter()
|
||||
.flat_map(|s| s.set_config.reserved_nodes.iter())
|
||||
{
|
||||
imp_p.insert(reserved.peer_id);
|
||||
}
|
||||
imp_p.shrink_to_fit();
|
||||
imp_p
|
||||
};
|
||||
let boot_node_ids = {
|
||||
let mut list = HashSet::new();
|
||||
for node in &network_config.boot_nodes {
|
||||
list.insert(node.peer_id);
|
||||
}
|
||||
list.shrink_to_fit();
|
||||
list
|
||||
};
|
||||
let default_peers_set_no_slot_peers = {
|
||||
let mut no_slot_p: HashSet<PeerId> = network_config
|
||||
.default_peers_set
|
||||
.reserved_nodes
|
||||
.iter()
|
||||
.map(|reserved| reserved.peer_id)
|
||||
.collect();
|
||||
no_slot_p.shrink_to_fit();
|
||||
no_slot_p
|
||||
};
|
||||
let default_peers_set_num_full = network_config.default_peers_set_num_full as usize;
|
||||
let default_peers_set_num_light = {
|
||||
let total = network_config.default_peers_set.out_peers +
|
||||
network_config.default_peers_set.in_peers;
|
||||
total.saturating_sub(network_config.default_peers_set_num_full) as usize
|
||||
};
|
||||
|
||||
let (chain_sync, block_announce_config) = ChainSync::new(
|
||||
mode,
|
||||
client.clone(),
|
||||
protocol_id,
|
||||
fork_id,
|
||||
roles,
|
||||
block_announce_validator,
|
||||
max_parallel_downloads,
|
||||
warp_sync_params,
|
||||
metrics_registry,
|
||||
network_service.clone(),
|
||||
import_queue,
|
||||
block_request_protocol_name,
|
||||
state_request_protocol_name,
|
||||
warp_sync_protocol_name,
|
||||
)?;
|
||||
|
||||
let block_announce_protocol_name = block_announce_config.notifications_protocol.clone();
|
||||
let (tx, service_rx) = tracing_unbounded("mpsc_chain_sync", 100_000);
|
||||
let num_connected = Arc::new(AtomicUsize::new(0));
|
||||
let is_major_syncing = Arc::new(AtomicBool::new(false));
|
||||
let genesis_hash = client
|
||||
.block_hash(0u32.into())
|
||||
.ok()
|
||||
.flatten()
|
||||
.expect("Genesis block exists; qed");
|
||||
|
||||
Ok((
|
||||
Self {
|
||||
roles,
|
||||
client,
|
||||
chain_sync,
|
||||
network_service,
|
||||
peers: HashMap::new(),
|
||||
block_announce_data_cache: LruCache::new(cache_capacity),
|
||||
block_announce_protocol_name,
|
||||
num_connected: num_connected.clone(),
|
||||
is_major_syncing: is_major_syncing.clone(),
|
||||
service_rx,
|
||||
genesis_hash,
|
||||
important_peers,
|
||||
default_peers_set_no_slot_connected_peers: HashSet::new(),
|
||||
boot_node_ids,
|
||||
default_peers_set_no_slot_peers,
|
||||
default_peers_set_num_full,
|
||||
default_peers_set_num_light,
|
||||
event_streams: Vec::new(),
|
||||
tick_timeout: Delay::new(TICK_TIMEOUT),
|
||||
metrics: if let Some(r) = metrics_registry {
|
||||
match Metrics::register(r, is_major_syncing.clone()) {
|
||||
Ok(metrics) => Some(metrics),
|
||||
Err(err) => {
|
||||
log::error!(target: "sync", "Failed to register metrics {err:?}");
|
||||
None
|
||||
},
|
||||
}
|
||||
} else {
|
||||
None
|
||||
},
|
||||
},
|
||||
SyncingService::new(tx, num_connected, is_major_syncing),
|
||||
block_announce_config,
|
||||
))
|
||||
}
|
||||
|
||||
/// Report Prometheus metrics.
|
||||
pub fn report_metrics(&self) {
|
||||
if let Some(metrics) = &self.metrics {
|
||||
let n = u64::try_from(self.peers.len()).unwrap_or(std::u64::MAX);
|
||||
metrics.peers.set(n);
|
||||
|
||||
let m = self.chain_sync.metrics();
|
||||
|
||||
metrics.fork_targets.set(m.fork_targets.into());
|
||||
metrics.queued_blocks.set(m.queued_blocks.into());
|
||||
|
||||
metrics
|
||||
.justifications
|
||||
.with_label_values(&["pending"])
|
||||
.set(m.justifications.pending_requests.into());
|
||||
metrics
|
||||
.justifications
|
||||
.with_label_values(&["active"])
|
||||
.set(m.justifications.active_requests.into());
|
||||
metrics
|
||||
.justifications
|
||||
.with_label_values(&["failed"])
|
||||
.set(m.justifications.failed_requests.into());
|
||||
metrics
|
||||
.justifications
|
||||
.with_label_values(&["importing"])
|
||||
.set(m.justifications.importing_requests.into());
|
||||
}
|
||||
}
|
||||
|
||||
fn update_peer_info(&mut self, who: &PeerId) {
|
||||
if let Some(info) = self.chain_sync.peer_info(who) {
|
||||
if let Some(ref mut peer) = self.peers.get_mut(who) {
|
||||
peer.info.best_hash = info.best_hash;
|
||||
peer.info.best_number = info.best_number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Process the result of the block announce validation.
|
||||
pub fn process_block_announce_validation_result(
|
||||
&mut self,
|
||||
validation_result: PollBlockAnnounceValidation<B::Header>,
|
||||
) {
|
||||
let (header, _is_best, who) = match validation_result {
|
||||
PollBlockAnnounceValidation::Skip => return,
|
||||
PollBlockAnnounceValidation::Nothing { is_best: _, who, announce } => {
|
||||
self.update_peer_info(&who);
|
||||
|
||||
if let Some(data) = announce.data {
|
||||
if !data.is_empty() {
|
||||
self.block_announce_data_cache.put(announce.header.hash(), data);
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
PollBlockAnnounceValidation::ImportHeader { announce, is_best, who } => {
|
||||
self.update_peer_info(&who);
|
||||
|
||||
if let Some(data) = announce.data {
|
||||
if !data.is_empty() {
|
||||
self.block_announce_data_cache.put(announce.header.hash(), data);
|
||||
}
|
||||
}
|
||||
|
||||
(announce.header, is_best, who)
|
||||
},
|
||||
PollBlockAnnounceValidation::Failure { who, disconnect } => {
|
||||
if disconnect {
|
||||
self.network_service
|
||||
.disconnect_peer(who, self.block_announce_protocol_name.clone());
|
||||
}
|
||||
|
||||
self.network_service.report_peer(who, rep::BAD_BLOCK_ANNOUNCEMENT);
|
||||
return
|
||||
},
|
||||
};
|
||||
|
||||
// to import header from announced block let's construct response to request that normally
|
||||
// would have been sent over network (but it is not in our case)
|
||||
let blocks_to_import = self.chain_sync.on_block_data(
|
||||
&who,
|
||||
None,
|
||||
BlockResponse {
|
||||
id: 0,
|
||||
blocks: vec![BlockData {
|
||||
hash: header.hash(),
|
||||
header: Some(header),
|
||||
body: None,
|
||||
indexed_body: None,
|
||||
receipt: None,
|
||||
message_queue: None,
|
||||
justification: None,
|
||||
justifications: None,
|
||||
}],
|
||||
},
|
||||
);
|
||||
|
||||
self.chain_sync.process_block_response_data(blocks_to_import);
|
||||
}
|
||||
|
||||
/// Push a block announce validation.
|
||||
///
|
||||
/// It is required that [`ChainSync::poll_block_announce_validation`] is
|
||||
/// called later to check for finished validations. The result of the validation
|
||||
/// needs to be passed to [`SyncingEngine::process_block_announce_validation_result`]
|
||||
/// to finish the processing.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This will internally create a future, but this future will not be registered
|
||||
/// in the task before being polled once. So, it is required to call
|
||||
/// [`ChainSync::poll_block_announce_validation`] to ensure that the future is
|
||||
/// registered properly and will wake up the task when being ready.
|
||||
pub fn push_block_announce_validation(
|
||||
&mut self,
|
||||
who: PeerId,
|
||||
announce: BlockAnnounce<B::Header>,
|
||||
) {
|
||||
let hash = announce.header.hash();
|
||||
|
||||
let peer = match self.peers.get_mut(&who) {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
log::error!(target: "sync", "Received block announce from disconnected peer {}", who);
|
||||
debug_assert!(false);
|
||||
return
|
||||
},
|
||||
};
|
||||
peer.known_blocks.insert(hash);
|
||||
|
||||
if peer.info.roles.is_full() {
|
||||
let is_best = match announce.state.unwrap_or(BlockState::Best) {
|
||||
BlockState::Best => true,
|
||||
BlockState::Normal => false,
|
||||
};
|
||||
|
||||
self.chain_sync.push_block_announce_validation(who, hash, announce, is_best);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn announce_block(&mut self, hash: B::Hash, data: Option<Vec<u8>>) {
|
||||
let header = match self.client.header(hash) {
|
||||
Ok(Some(header)) => header,
|
||||
Ok(None) => {
|
||||
log::warn!(target: "sync", "Trying to announce unknown block: {}", hash);
|
||||
return
|
||||
},
|
||||
Err(e) => {
|
||||
log::warn!(target: "sync", "Error reading block header {}: {}", hash, e);
|
||||
return
|
||||
},
|
||||
};
|
||||
|
||||
// don't announce genesis block since it will be ignored
|
||||
if header.number().is_zero() {
|
||||
return
|
||||
}
|
||||
|
||||
let is_best = self.client.info().best_hash == hash;
|
||||
log::debug!(target: "sync", "Reannouncing block {:?} is_best: {}", hash, is_best);
|
||||
|
||||
let data = data
|
||||
.or_else(|| self.block_announce_data_cache.get(&hash).cloned())
|
||||
.unwrap_or_default();
|
||||
|
||||
for (who, ref mut peer) in self.peers.iter_mut() {
|
||||
let inserted = peer.known_blocks.insert(hash);
|
||||
if inserted {
|
||||
log::trace!(target: "sync", "Announcing block {:?} to {}", hash, who);
|
||||
let message = BlockAnnounce {
|
||||
header: header.clone(),
|
||||
state: if is_best { Some(BlockState::Best) } else { Some(BlockState::Normal) },
|
||||
data: Some(data.clone()),
|
||||
};
|
||||
|
||||
self.network_service.write_notification(
|
||||
*who,
|
||||
self.block_announce_protocol_name.clone(),
|
||||
message.encode(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inform sync about new best imported block.
|
||||
pub fn new_best_block_imported(&mut self, hash: B::Hash, number: NumberFor<B>) {
|
||||
log::debug!(target: "sync", "New best block imported {:?}/#{}", hash, number);
|
||||
|
||||
self.chain_sync.update_chain_info(&hash, number);
|
||||
self.network_service.set_notification_handshake(
|
||||
self.block_announce_protocol_name.clone(),
|
||||
BlockAnnouncesHandshake::<B>::build(self.roles, number, hash, self.genesis_hash)
|
||||
.encode(),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn run(mut self, mut stream: Pin<Box<dyn Stream<Item = Event> + Send>>) {
|
||||
loop {
|
||||
futures::future::poll_fn(|cx| self.poll(cx, &mut stream)).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll(
|
||||
&mut self,
|
||||
cx: &mut std::task::Context,
|
||||
event_stream: &mut Pin<Box<dyn Stream<Item = Event> + Send>>,
|
||||
) -> Poll<()> {
|
||||
self.num_connected.store(self.peers.len(), Ordering::Relaxed);
|
||||
self.is_major_syncing
|
||||
.store(self.chain_sync.status().state.is_major_syncing(), Ordering::Relaxed);
|
||||
|
||||
while let Poll::Ready(()) = self.tick_timeout.poll_unpin(cx) {
|
||||
self.report_metrics();
|
||||
self.tick_timeout.reset(TICK_TIMEOUT);
|
||||
}
|
||||
|
||||
while let Poll::Ready(Some(event)) = event_stream.poll_next_unpin(cx) {
|
||||
match event {
|
||||
Event::NotificationStreamOpened {
|
||||
remote, protocol, received_handshake, ..
|
||||
} => {
|
||||
if protocol != self.block_announce_protocol_name {
|
||||
continue
|
||||
}
|
||||
|
||||
match <BlockAnnouncesHandshake<B> as DecodeAll>::decode_all(
|
||||
&mut &received_handshake[..],
|
||||
) {
|
||||
Ok(handshake) => {
|
||||
if self.on_sync_peer_connected(remote, handshake).is_err() {
|
||||
log::debug!(
|
||||
target: "sync",
|
||||
"Failed to register peer {remote:?}: {received_handshake:?}",
|
||||
);
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
log::debug!(
|
||||
target: "sync",
|
||||
"Couldn't decode handshake sent by {}: {:?}: {}",
|
||||
remote,
|
||||
received_handshake,
|
||||
err,
|
||||
);
|
||||
self.network_service.report_peer(remote, rep::BAD_MESSAGE);
|
||||
},
|
||||
}
|
||||
},
|
||||
Event::NotificationStreamClosed { remote, protocol } => {
|
||||
if protocol != self.block_announce_protocol_name {
|
||||
continue
|
||||
}
|
||||
|
||||
if self.on_sync_peer_disconnected(remote).is_err() {
|
||||
log::trace!(
|
||||
target: "sync",
|
||||
"Disconnected peer which had earlier been refused by on_sync_peer_connected {}",
|
||||
remote
|
||||
);
|
||||
}
|
||||
},
|
||||
Event::NotificationsReceived { remote, messages } => {
|
||||
for (protocol, message) in messages {
|
||||
if protocol != self.block_announce_protocol_name {
|
||||
continue
|
||||
}
|
||||
|
||||
if self.peers.contains_key(&remote) {
|
||||
if let Ok(announce) = BlockAnnounce::decode(&mut message.as_ref()) {
|
||||
self.push_block_announce_validation(remote, announce);
|
||||
|
||||
// Make sure that the newly added block announce validation future
|
||||
// was polled once to be registered in the task.
|
||||
if let Poll::Ready(res) =
|
||||
self.chain_sync.poll_block_announce_validation(cx)
|
||||
{
|
||||
self.process_block_announce_validation_result(res)
|
||||
}
|
||||
} else {
|
||||
log::warn!(target: "sub-libp2p", "Failed to decode block announce");
|
||||
}
|
||||
} else {
|
||||
log::trace!(
|
||||
target: "sync",
|
||||
"Received sync for peer earlier refused by sync layer: {}",
|
||||
remote
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
while let Poll::Ready(Some(event)) = self.service_rx.poll_next_unpin(cx) {
|
||||
match event {
|
||||
ToServiceCommand::SetSyncForkRequest(peers, hash, number) => {
|
||||
self.chain_sync.set_sync_fork_request(peers, &hash, number);
|
||||
},
|
||||
ToServiceCommand::EventStream(tx) => self.event_streams.push(tx),
|
||||
ToServiceCommand::RequestJustification(hash, number) =>
|
||||
self.chain_sync.request_justification(&hash, number),
|
||||
ToServiceCommand::ClearJustificationRequests =>
|
||||
self.chain_sync.clear_justification_requests(),
|
||||
ToServiceCommand::BlocksProcessed(imported, count, results) => {
|
||||
for result in self.chain_sync.on_blocks_processed(imported, count, results) {
|
||||
match result {
|
||||
Ok((id, req)) => self.chain_sync.send_block_request(id, req),
|
||||
Err(BadPeer(id, repu)) => {
|
||||
self.network_service
|
||||
.disconnect_peer(id, self.block_announce_protocol_name.clone());
|
||||
self.network_service.report_peer(id, repu)
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
ToServiceCommand::JustificationImported(peer, hash, number, success) => {
|
||||
self.chain_sync.on_justification_import(hash, number, success);
|
||||
if !success {
|
||||
log::info!(target: "sync", "💔 Invalid justification provided by {} for #{}", peer, hash);
|
||||
self.network_service
|
||||
.disconnect_peer(peer, self.block_announce_protocol_name.clone());
|
||||
self.network_service.report_peer(
|
||||
peer,
|
||||
sc_peerset::ReputationChange::new_fatal("Invalid justification"),
|
||||
);
|
||||
}
|
||||
},
|
||||
ToServiceCommand::AnnounceBlock(hash, data) => self.announce_block(hash, data),
|
||||
ToServiceCommand::NewBestBlockImported(hash, number) =>
|
||||
self.new_best_block_imported(hash, number),
|
||||
ToServiceCommand::Status(tx) => {
|
||||
let _ = tx.send(self.chain_sync.status());
|
||||
},
|
||||
ToServiceCommand::NumActivePeers(tx) => {
|
||||
let _ = tx.send(self.chain_sync.num_active_peers());
|
||||
},
|
||||
ToServiceCommand::SyncState(tx) => {
|
||||
let _ = tx.send(self.chain_sync.status());
|
||||
},
|
||||
ToServiceCommand::BestSeenBlock(tx) => {
|
||||
let _ = tx.send(self.chain_sync.status().best_seen_block);
|
||||
},
|
||||
ToServiceCommand::NumSyncPeers(tx) => {
|
||||
let _ = tx.send(self.chain_sync.status().num_peers);
|
||||
},
|
||||
ToServiceCommand::NumQueuedBlocks(tx) => {
|
||||
let _ = tx.send(self.chain_sync.status().queued_blocks);
|
||||
},
|
||||
ToServiceCommand::NumDownloadedBlocks(tx) => {
|
||||
let _ = tx.send(self.chain_sync.num_downloaded_blocks());
|
||||
},
|
||||
ToServiceCommand::NumSyncRequests(tx) => {
|
||||
let _ = tx.send(self.chain_sync.num_sync_requests());
|
||||
},
|
||||
ToServiceCommand::PeersInfo(tx) => {
|
||||
let peers_info =
|
||||
self.peers.iter().map(|(id, peer)| (*id, peer.info.clone())).collect();
|
||||
let _ = tx.send(peers_info);
|
||||
},
|
||||
ToServiceCommand::OnBlockFinalized(hash, header) =>
|
||||
self.chain_sync.on_block_finalized(&hash, *header.number()),
|
||||
}
|
||||
}
|
||||
|
||||
while let Poll::Ready(result) = self.chain_sync.poll(cx) {
|
||||
self.process_block_announce_validation_result(result);
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
|
||||
/// Called by peer when it is disconnecting.
|
||||
///
|
||||
/// Returns a result if the handshake of this peer was indeed accepted.
|
||||
pub fn on_sync_peer_disconnected(&mut self, peer: PeerId) -> Result<(), ()> {
|
||||
if self.important_peers.contains(&peer) {
|
||||
log::warn!(target: "sync", "Reserved peer {} disconnected", peer);
|
||||
} else {
|
||||
log::debug!(target: "sync", "{} disconnected", peer);
|
||||
}
|
||||
|
||||
if self.peers.remove(&peer).is_some() {
|
||||
self.chain_sync.peer_disconnected(&peer);
|
||||
self.default_peers_set_no_slot_connected_peers.remove(&peer);
|
||||
self.event_streams
|
||||
.retain(|stream| stream.unbounded_send(SyncEvent::PeerDisconnected(peer)).is_ok());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Called on the first connection between two peers on the default set, after their exchange
|
||||
/// of handshake.
|
||||
///
|
||||
/// Returns `Ok` if the handshake is accepted and the peer added to the list of peers we sync
|
||||
/// from.
|
||||
pub fn on_sync_peer_connected(
|
||||
&mut self,
|
||||
who: PeerId,
|
||||
status: BlockAnnouncesHandshake<B>,
|
||||
) -> Result<(), ()> {
|
||||
log::trace!(target: "sync", "New peer {} {:?}", who, status);
|
||||
|
||||
if self.peers.contains_key(&who) {
|
||||
log::error!(target: "sync", "Called on_sync_peer_connected with already connected peer {}", who);
|
||||
debug_assert!(false);
|
||||
return Err(())
|
||||
}
|
||||
|
||||
if status.genesis_hash != self.genesis_hash {
|
||||
self.network_service.report_peer(who, rep::GENESIS_MISMATCH);
|
||||
self.network_service
|
||||
.disconnect_peer(who, self.block_announce_protocol_name.clone());
|
||||
|
||||
if self.important_peers.contains(&who) {
|
||||
log::error!(
|
||||
target: "sync",
|
||||
"Reserved peer id `{}` is on a different chain (our genesis: {} theirs: {})",
|
||||
who,
|
||||
self.genesis_hash,
|
||||
status.genesis_hash,
|
||||
);
|
||||
} else if self.boot_node_ids.contains(&who) {
|
||||
log::error!(
|
||||
target: "sync",
|
||||
"Bootnode with peer id `{}` is on a different chain (our genesis: {} theirs: {})",
|
||||
who,
|
||||
self.genesis_hash,
|
||||
status.genesis_hash,
|
||||
);
|
||||
} else {
|
||||
log::debug!(
|
||||
target: "sync",
|
||||
"Peer is on different chain (our genesis: {} theirs: {})",
|
||||
self.genesis_hash, status.genesis_hash
|
||||
);
|
||||
}
|
||||
|
||||
return Err(())
|
||||
}
|
||||
|
||||
if self.roles.is_light() {
|
||||
// we're not interested in light peers
|
||||
if status.roles.is_light() {
|
||||
log::debug!(target: "sync", "Peer {} is unable to serve light requests", who);
|
||||
self.network_service.report_peer(who, rep::BAD_ROLE);
|
||||
self.network_service
|
||||
.disconnect_peer(who, self.block_announce_protocol_name.clone());
|
||||
return Err(())
|
||||
}
|
||||
|
||||
// we don't interested in peers that are far behind us
|
||||
let self_best_block = self.client.info().best_number;
|
||||
let blocks_difference = self_best_block
|
||||
.checked_sub(&status.best_number)
|
||||
.unwrap_or_else(Zero::zero)
|
||||
.saturated_into::<u64>();
|
||||
if blocks_difference > LIGHT_MAXIMAL_BLOCKS_DIFFERENCE {
|
||||
log::debug!(target: "sync", "Peer {} is far behind us and will unable to serve light requests", who);
|
||||
self.network_service.report_peer(who, rep::PEER_BEHIND_US_LIGHT);
|
||||
self.network_service
|
||||
.disconnect_peer(who, self.block_announce_protocol_name.clone());
|
||||
return Err(())
|
||||
}
|
||||
}
|
||||
|
||||
let no_slot_peer = self.default_peers_set_no_slot_peers.contains(&who);
|
||||
let this_peer_reserved_slot: usize = if no_slot_peer { 1 } else { 0 };
|
||||
|
||||
if status.roles.is_full() &&
|
||||
self.chain_sync.num_peers() >=
|
||||
self.default_peers_set_num_full +
|
||||
self.default_peers_set_no_slot_connected_peers.len() +
|
||||
this_peer_reserved_slot
|
||||
{
|
||||
log::debug!(target: "sync", "Too many full nodes, rejecting {}", who);
|
||||
self.network_service
|
||||
.disconnect_peer(who, self.block_announce_protocol_name.clone());
|
||||
return Err(())
|
||||
}
|
||||
|
||||
if status.roles.is_light() &&
|
||||
(self.peers.len() - self.chain_sync.num_peers()) >= self.default_peers_set_num_light
|
||||
{
|
||||
// Make sure that not all slots are occupied by light clients.
|
||||
log::debug!(target: "sync", "Too many light nodes, rejecting {}", who);
|
||||
self.network_service
|
||||
.disconnect_peer(who, self.block_announce_protocol_name.clone());
|
||||
return Err(())
|
||||
}
|
||||
|
||||
let peer = Peer {
|
||||
info: ExtendedPeerInfo {
|
||||
roles: status.roles,
|
||||
best_hash: status.best_hash,
|
||||
best_number: status.best_number,
|
||||
},
|
||||
known_blocks: LruHashSet::new(
|
||||
NonZeroUsize::new(MAX_KNOWN_BLOCKS).expect("Constant is nonzero"),
|
||||
),
|
||||
};
|
||||
|
||||
let req = if peer.info.roles.is_full() {
|
||||
match self.chain_sync.new_peer(who, peer.info.best_hash, peer.info.best_number) {
|
||||
Ok(req) => req,
|
||||
Err(BadPeer(id, repu)) => {
|
||||
self.network_service
|
||||
.disconnect_peer(id, self.block_announce_protocol_name.clone());
|
||||
self.network_service.report_peer(id, repu);
|
||||
return Err(())
|
||||
},
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
log::debug!(target: "sync", "Connected {}", who);
|
||||
|
||||
self.peers.insert(who, peer);
|
||||
if no_slot_peer {
|
||||
self.default_peers_set_no_slot_connected_peers.insert(who);
|
||||
}
|
||||
|
||||
if let Some(req) = req {
|
||||
self.chain_sync.send_block_request(who, req);
|
||||
}
|
||||
|
||||
self.event_streams
|
||||
.retain(|stream| stream.unbounded_send(SyncEvent::PeerConnected(who)).is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -30,20 +30,18 @@
|
||||
|
||||
pub mod block_request_handler;
|
||||
pub mod blocks;
|
||||
pub mod engine;
|
||||
pub mod mock;
|
||||
mod schema;
|
||||
pub mod service;
|
||||
pub mod state;
|
||||
pub mod state_request_handler;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub mod warp;
|
||||
pub mod warp_request_handler;
|
||||
|
||||
use crate::{
|
||||
blocks::BlockCollection,
|
||||
schema::v1::{StateRequest, StateResponse},
|
||||
service::chain_sync::{ChainSyncInterfaceHandle, ToServiceCommand},
|
||||
state::StateSync,
|
||||
warp::{WarpProofImportResult, WarpSync},
|
||||
};
|
||||
@@ -78,7 +76,7 @@ use sc_network_common::{
|
||||
SyncState, SyncStatus,
|
||||
},
|
||||
};
|
||||
use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver};
|
||||
pub use service::chain_sync::SyncingService;
|
||||
use sp_arithmetic::traits::Saturating;
|
||||
use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata};
|
||||
use sp_consensus::{
|
||||
@@ -327,8 +325,6 @@ pub struct ChainSync<B: BlockT, Client> {
|
||||
import_existing: bool,
|
||||
/// Gap download process.
|
||||
gap_sync: Option<GapSync<B>>,
|
||||
/// Channel for receiving service commands
|
||||
service_rx: TracingUnboundedReceiver<ToServiceCommand<B>>,
|
||||
/// Handle for communicating with `NetworkService`
|
||||
network_service: service::network::NetworkServiceHandle,
|
||||
/// Protocol name used for block announcements
|
||||
@@ -1326,47 +1322,6 @@ where
|
||||
&mut self,
|
||||
cx: &mut std::task::Context,
|
||||
) -> Poll<PollBlockAnnounceValidation<B::Header>> {
|
||||
while let Poll::Ready(Some(event)) = self.service_rx.poll_next_unpin(cx) {
|
||||
match event {
|
||||
ToServiceCommand::SetSyncForkRequest(peers, hash, number) => {
|
||||
self.set_sync_fork_request(peers, &hash, number);
|
||||
},
|
||||
ToServiceCommand::RequestJustification(hash, number) =>
|
||||
self.request_justification(&hash, number),
|
||||
ToServiceCommand::ClearJustificationRequests => self.clear_justification_requests(),
|
||||
ToServiceCommand::BlocksProcessed(imported, count, results) => {
|
||||
for result in self.on_blocks_processed(imported, count, results) {
|
||||
match result {
|
||||
Ok((id, req)) => self.send_block_request(id, req),
|
||||
Err(BadPeer(id, repu)) => {
|
||||
self.network_service
|
||||
.disconnect_peer(id, self.block_announce_protocol_name.clone());
|
||||
self.network_service.report_peer(id, repu)
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
ToServiceCommand::JustificationImported(peer, hash, number, success) => {
|
||||
self.on_justification_import(hash, number, success);
|
||||
if !success {
|
||||
info!(target: "sync", "💔 Invalid justification provided by {} for #{}", peer, hash);
|
||||
self.network_service
|
||||
.disconnect_peer(peer, self.block_announce_protocol_name.clone());
|
||||
self.network_service.report_peer(
|
||||
peer,
|
||||
sc_peerset::ReputationChange::new_fatal("Invalid justification"),
|
||||
);
|
||||
}
|
||||
},
|
||||
ToServiceCommand::BlockFinalized(hash, number) => {
|
||||
self.on_block_finalized(&hash, number);
|
||||
},
|
||||
ToServiceCommand::Status { pending_response } => {
|
||||
let _ = pending_response.send(self.status());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Should be called before `process_outbound_requests` to ensure
|
||||
// that a potential target block is directly leading to requests.
|
||||
if let Some(warp_sync) = &mut self.warp_sync {
|
||||
@@ -1448,8 +1403,7 @@ where
|
||||
block_request_protocol_name: ProtocolName,
|
||||
state_request_protocol_name: ProtocolName,
|
||||
warp_sync_protocol_name: Option<ProtocolName>,
|
||||
) -> Result<(Self, ChainSyncInterfaceHandle<B>, NonDefaultSetConfig), ClientError> {
|
||||
let (tx, service_rx) = tracing_unbounded("mpsc_chain_sync", 100_000);
|
||||
) -> Result<(Self, NonDefaultSetConfig), ClientError> {
|
||||
let block_announce_config = Self::get_block_announce_proto_config(
|
||||
protocol_id,
|
||||
fork_id,
|
||||
@@ -1483,7 +1437,6 @@ where
|
||||
warp_sync: None,
|
||||
import_existing: false,
|
||||
gap_sync: None,
|
||||
service_rx,
|
||||
network_service,
|
||||
block_request_protocol_name,
|
||||
state_request_protocol_name,
|
||||
@@ -1509,7 +1462,7 @@ where
|
||||
};
|
||||
|
||||
sync.reset_sync_start_point()?;
|
||||
Ok((sync, ChainSyncInterfaceHandle::new(tx), block_announce_config))
|
||||
Ok((sync, block_announce_config))
|
||||
}
|
||||
|
||||
/// Returns the median seen block number.
|
||||
@@ -3231,7 +3184,7 @@ mod test {
|
||||
let import_queue = Box::new(sc_consensus::import_queue::mock::MockImportQueueHandle::new());
|
||||
let (_chain_sync_network_provider, chain_sync_network_handle) =
|
||||
NetworkServiceProvider::new();
|
||||
let (mut sync, _, _) = ChainSync::new(
|
||||
let (mut sync, _) = ChainSync::new(
|
||||
SyncMode::Full,
|
||||
client.clone(),
|
||||
ProtocolId::from("test-protocol-name"),
|
||||
@@ -3297,7 +3250,7 @@ mod test {
|
||||
let (_chain_sync_network_provider, chain_sync_network_handle) =
|
||||
NetworkServiceProvider::new();
|
||||
|
||||
let (mut sync, _, _) = ChainSync::new(
|
||||
let (mut sync, _) = ChainSync::new(
|
||||
SyncMode::Full,
|
||||
client.clone(),
|
||||
ProtocolId::from("test-protocol-name"),
|
||||
@@ -3478,7 +3431,7 @@ mod test {
|
||||
let (_chain_sync_network_provider, chain_sync_network_handle) =
|
||||
NetworkServiceProvider::new();
|
||||
|
||||
let (mut sync, _, _) = ChainSync::new(
|
||||
let (mut sync, _) = ChainSync::new(
|
||||
SyncMode::Full,
|
||||
client.clone(),
|
||||
ProtocolId::from("test-protocol-name"),
|
||||
@@ -3604,7 +3557,7 @@ mod test {
|
||||
NetworkServiceProvider::new();
|
||||
let info = client.info();
|
||||
|
||||
let (mut sync, _, _) = ChainSync::new(
|
||||
let (mut sync, _) = ChainSync::new(
|
||||
SyncMode::Full,
|
||||
client.clone(),
|
||||
ProtocolId::from("test-protocol-name"),
|
||||
@@ -3760,7 +3713,7 @@ mod test {
|
||||
|
||||
let info = client.info();
|
||||
|
||||
let (mut sync, _, _) = ChainSync::new(
|
||||
let (mut sync, _) = ChainSync::new(
|
||||
SyncMode::Full,
|
||||
client.clone(),
|
||||
ProtocolId::from("test-protocol-name"),
|
||||
@@ -3901,7 +3854,7 @@ mod test {
|
||||
|
||||
let info = client.info();
|
||||
|
||||
let (mut sync, _, _) = ChainSync::new(
|
||||
let (mut sync, _) = ChainSync::new(
|
||||
SyncMode::Full,
|
||||
client.clone(),
|
||||
ProtocolId::from("test-protocol-name"),
|
||||
@@ -4042,7 +3995,7 @@ mod test {
|
||||
let mut client = Arc::new(TestClientBuilder::new().build());
|
||||
let blocks = (0..3).map(|_| build_block(&mut client, None, false)).collect::<Vec<_>>();
|
||||
|
||||
let (mut sync, _, _) = ChainSync::new(
|
||||
let (mut sync, _) = ChainSync::new(
|
||||
SyncMode::Full,
|
||||
client.clone(),
|
||||
ProtocolId::from("test-protocol-name"),
|
||||
@@ -4087,7 +4040,7 @@ mod test {
|
||||
|
||||
let empty_client = Arc::new(TestClientBuilder::new().build());
|
||||
|
||||
let (mut sync, _, _) = ChainSync::new(
|
||||
let (mut sync, _) = ChainSync::new(
|
||||
SyncMode::Full,
|
||||
empty_client.clone(),
|
||||
ProtocolId::from("test-protocol-name"),
|
||||
|
||||
@@ -16,15 +16,26 @@
|
||||
// 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 futures::channel::oneshot;
|
||||
use futures::{channel::oneshot, Stream};
|
||||
use libp2p::PeerId;
|
||||
|
||||
use sc_consensus::{BlockImportError, BlockImportStatus, JustificationSyncLink, Link};
|
||||
use sc_network_common::{service::NetworkSyncForkRequest, sync::SyncStatus};
|
||||
use sc_utils::mpsc::TracingUnboundedSender;
|
||||
use sc_network_common::{
|
||||
service::{NetworkBlock, NetworkSyncForkRequest},
|
||||
sync::{ExtendedPeerInfo, SyncEvent, SyncEventStream, SyncStatus, SyncStatusProvider},
|
||||
};
|
||||
use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender};
|
||||
use sp_runtime::traits::{Block as BlockT, NumberFor};
|
||||
|
||||
use std::{
|
||||
pin::Pin,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
/// Commands send to `ChainSync`
|
||||
#[derive(Debug)]
|
||||
pub enum ToServiceCommand<B: BlockT> {
|
||||
SetSyncForkRequest(Vec<PeerId>, B::Hash, NumberFor<B>),
|
||||
RequestJustification(B::Hash, NumberFor<B>),
|
||||
@@ -35,27 +46,105 @@ pub enum ToServiceCommand<B: BlockT> {
|
||||
Vec<(Result<BlockImportStatus<NumberFor<B>>, BlockImportError>, B::Hash)>,
|
||||
),
|
||||
JustificationImported(PeerId, B::Hash, NumberFor<B>, bool),
|
||||
BlockFinalized(B::Hash, NumberFor<B>),
|
||||
Status {
|
||||
pending_response: oneshot::Sender<SyncStatus<B>>,
|
||||
},
|
||||
AnnounceBlock(B::Hash, Option<Vec<u8>>),
|
||||
NewBestBlockImported(B::Hash, NumberFor<B>),
|
||||
EventStream(TracingUnboundedSender<SyncEvent>),
|
||||
Status(oneshot::Sender<SyncStatus<B>>),
|
||||
NumActivePeers(oneshot::Sender<usize>),
|
||||
SyncState(oneshot::Sender<SyncStatus<B>>),
|
||||
BestSeenBlock(oneshot::Sender<Option<NumberFor<B>>>),
|
||||
NumSyncPeers(oneshot::Sender<u32>),
|
||||
NumQueuedBlocks(oneshot::Sender<u32>),
|
||||
NumDownloadedBlocks(oneshot::Sender<usize>),
|
||||
NumSyncRequests(oneshot::Sender<usize>),
|
||||
PeersInfo(oneshot::Sender<Vec<(PeerId, ExtendedPeerInfo<B>)>>),
|
||||
OnBlockFinalized(B::Hash, B::Header),
|
||||
// Status {
|
||||
// pending_response: oneshot::Sender<SyncStatus<B>>,
|
||||
// },
|
||||
}
|
||||
|
||||
/// Handle for communicating with `ChainSync` asynchronously
|
||||
#[derive(Clone)]
|
||||
pub struct ChainSyncInterfaceHandle<B: BlockT> {
|
||||
pub struct SyncingService<B: BlockT> {
|
||||
tx: TracingUnboundedSender<ToServiceCommand<B>>,
|
||||
/// Number of peers we're connected to.
|
||||
num_connected: Arc<AtomicUsize>,
|
||||
/// Are we actively catching up with the chain?
|
||||
is_major_syncing: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl<B: BlockT> ChainSyncInterfaceHandle<B> {
|
||||
impl<B: BlockT> SyncingService<B> {
|
||||
/// Create new handle
|
||||
pub fn new(tx: TracingUnboundedSender<ToServiceCommand<B>>) -> Self {
|
||||
Self { tx }
|
||||
pub fn new(
|
||||
tx: TracingUnboundedSender<ToServiceCommand<B>>,
|
||||
num_connected: Arc<AtomicUsize>,
|
||||
is_major_syncing: Arc<AtomicBool>,
|
||||
) -> Self {
|
||||
Self { tx, num_connected, is_major_syncing }
|
||||
}
|
||||
|
||||
/// Notify ChainSync about finalized block
|
||||
pub fn on_block_finalized(&self, hash: B::Hash, number: NumberFor<B>) {
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::BlockFinalized(hash, number));
|
||||
/// Get the number of active peers.
|
||||
pub async fn num_active_peers(&self) -> Result<usize, oneshot::Canceled> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::NumActivePeers(tx));
|
||||
|
||||
rx.await
|
||||
}
|
||||
|
||||
/// Get best seen block.
|
||||
pub async fn best_seen_block(&self) -> Result<Option<NumberFor<B>>, oneshot::Canceled> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::BestSeenBlock(tx));
|
||||
|
||||
rx.await
|
||||
}
|
||||
|
||||
/// Get the number of sync peers.
|
||||
pub async fn num_sync_peers(&self) -> Result<u32, oneshot::Canceled> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::NumSyncPeers(tx));
|
||||
|
||||
rx.await
|
||||
}
|
||||
|
||||
/// Get the number of queued blocks.
|
||||
pub async fn num_queued_blocks(&self) -> Result<u32, oneshot::Canceled> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::NumQueuedBlocks(tx));
|
||||
|
||||
rx.await
|
||||
}
|
||||
|
||||
/// Get the number of downloaded blocks.
|
||||
pub async fn num_downloaded_blocks(&self) -> Result<usize, oneshot::Canceled> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::NumDownloadedBlocks(tx));
|
||||
|
||||
rx.await
|
||||
}
|
||||
|
||||
/// Get the number of sync requests.
|
||||
pub async fn num_sync_requests(&self) -> Result<usize, oneshot::Canceled> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::NumSyncRequests(tx));
|
||||
|
||||
rx.await
|
||||
}
|
||||
|
||||
/// Get peer information.
|
||||
pub async fn peers_info(
|
||||
&self,
|
||||
) -> Result<Vec<(PeerId, ExtendedPeerInfo<B>)>, oneshot::Canceled> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::PeersInfo(tx));
|
||||
|
||||
rx.await
|
||||
}
|
||||
|
||||
/// Notify the `SyncingEngine` that a block has been finalized.
|
||||
pub fn on_block_finalized(&self, hash: B::Hash, header: B::Header) {
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::OnBlockFinalized(hash, header));
|
||||
}
|
||||
|
||||
/// Get sync status
|
||||
@@ -63,15 +152,13 @@ impl<B: BlockT> ChainSyncInterfaceHandle<B> {
|
||||
/// Returns an error if `ChainSync` has terminated.
|
||||
pub async fn status(&self) -> Result<SyncStatus<B>, ()> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::Status { pending_response: tx });
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::Status(tx));
|
||||
|
||||
rx.await.map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BlockT + 'static> NetworkSyncForkRequest<B::Hash, NumberFor<B>>
|
||||
for ChainSyncInterfaceHandle<B>
|
||||
{
|
||||
impl<B: BlockT + 'static> NetworkSyncForkRequest<B::Hash, NumberFor<B>> for SyncingService<B> {
|
||||
/// Configure an explicit fork sync request.
|
||||
///
|
||||
/// Note that this function should not be used for recent blocks.
|
||||
@@ -87,7 +174,7 @@ impl<B: BlockT + 'static> NetworkSyncForkRequest<B::Hash, NumberFor<B>>
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BlockT> JustificationSyncLink<B> for ChainSyncInterfaceHandle<B> {
|
||||
impl<B: BlockT> JustificationSyncLink<B> for SyncingService<B> {
|
||||
/// Request a justification for the given block from the network.
|
||||
///
|
||||
/// On success, the justification will be passed to the import queue that was part at
|
||||
@@ -101,7 +188,18 @@ impl<B: BlockT> JustificationSyncLink<B> for ChainSyncInterfaceHandle<B> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BlockT> Link<B> for ChainSyncInterfaceHandle<B> {
|
||||
#[async_trait::async_trait]
|
||||
impl<B: BlockT> SyncStatusProvider<B> for SyncingService<B> {
|
||||
/// Get high-level view of the syncing status.
|
||||
async fn status(&self) -> Result<SyncStatus<B>, ()> {
|
||||
let (rtx, rrx) = oneshot::channel();
|
||||
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::Status(rtx));
|
||||
rrx.await.map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BlockT> Link<B> for SyncingService<B> {
|
||||
fn blocks_processed(
|
||||
&mut self,
|
||||
imported: usize,
|
||||
@@ -129,3 +227,32 @@ impl<B: BlockT> Link<B> for ChainSyncInterfaceHandle<B> {
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::RequestJustification(*hash, number));
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BlockT> SyncEventStream for SyncingService<B> {
|
||||
/// Get syncing event stream.
|
||||
fn event_stream(&self, name: &'static str) -> Pin<Box<dyn Stream<Item = SyncEvent> + Send>> {
|
||||
let (tx, rx) = tracing_unbounded(name, 100_000);
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::EventStream(tx));
|
||||
Box::pin(rx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BlockT> NetworkBlock<B::Hash, NumberFor<B>> for SyncingService<B> {
|
||||
fn announce_block(&self, hash: B::Hash, data: Option<Vec<u8>>) {
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::AnnounceBlock(hash, data));
|
||||
}
|
||||
|
||||
fn new_best_block_imported(&self, hash: B::Hash, number: NumberFor<B>) {
|
||||
let _ = self.tx.unbounded_send(ToServiceCommand::NewBestBlockImported(hash, number));
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BlockT> sp_consensus::SyncOracle for SyncingService<B> {
|
||||
fn is_major_syncing(&self) -> bool {
|
||||
self.is_major_syncing.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn is_offline(&self) -> bool {
|
||||
self.num_connected.load(Ordering::Relaxed) == 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,10 @@ use sc_network_common::{
|
||||
config::MultiaddrWithPeerId,
|
||||
protocol::ProtocolName,
|
||||
request_responses::{IfDisconnected, RequestFailure},
|
||||
service::{NetworkPeers, NetworkRequest, NetworkSyncForkRequest},
|
||||
service::{
|
||||
NetworkNotification, NetworkPeers, NetworkRequest, NetworkSyncForkRequest,
|
||||
NotificationSender, NotificationSenderError,
|
||||
},
|
||||
};
|
||||
use sc_peerset::ReputationChange;
|
||||
use sp_runtime::traits::{Block as BlockT, NumberFor};
|
||||
@@ -125,4 +128,14 @@ mockall::mock! {
|
||||
connect: IfDisconnected,
|
||||
);
|
||||
}
|
||||
|
||||
impl NetworkNotification for Network {
|
||||
fn write_notification(&self, target: PeerId, protocol: ProtocolName, message: Vec<u8>);
|
||||
fn notification_sender(
|
||||
&self,
|
||||
target: PeerId,
|
||||
protocol: ProtocolName,
|
||||
) -> Result<Box<dyn NotificationSender>, NotificationSenderError>;
|
||||
fn set_notification_handshake(&self, protocol: ProtocolName, handshake: Vec<u8>);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,16 +21,16 @@ use libp2p::PeerId;
|
||||
use sc_network_common::{
|
||||
protocol::ProtocolName,
|
||||
request_responses::{IfDisconnected, RequestFailure},
|
||||
service::{NetworkPeers, NetworkRequest},
|
||||
service::{NetworkNotification, NetworkPeers, NetworkRequest},
|
||||
};
|
||||
use sc_peerset::ReputationChange;
|
||||
use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Network-related services required by `sc-network-sync`
|
||||
pub trait Network: NetworkPeers + NetworkRequest {}
|
||||
pub trait Network: NetworkPeers + NetworkRequest + NetworkNotification {}
|
||||
|
||||
impl<T> Network for T where T: NetworkPeers + NetworkRequest {}
|
||||
impl<T> Network for T where T: NetworkPeers + NetworkRequest + NetworkNotification {}
|
||||
|
||||
/// Network service provider for `ChainSync`
|
||||
///
|
||||
@@ -56,6 +56,12 @@ pub enum ToServiceCommand {
|
||||
oneshot::Sender<Result<Vec<u8>, RequestFailure>>,
|
||||
IfDisconnected,
|
||||
),
|
||||
|
||||
/// Call `NetworkNotification::write_notification()`
|
||||
WriteNotification(PeerId, ProtocolName, Vec<u8>),
|
||||
|
||||
/// Call `NetworkNotification::set_notification_handshake()`
|
||||
SetNotificationHandshake(ProtocolName, Vec<u8>),
|
||||
}
|
||||
|
||||
/// Handle that is (temporarily) passed to `ChainSync` so it can
|
||||
@@ -94,6 +100,20 @@ impl NetworkServiceHandle {
|
||||
.tx
|
||||
.unbounded_send(ToServiceCommand::StartRequest(who, protocol, request, tx, connect));
|
||||
}
|
||||
|
||||
/// Send notification to peer
|
||||
pub fn write_notification(&self, who: PeerId, protocol: ProtocolName, message: Vec<u8>) {
|
||||
let _ = self
|
||||
.tx
|
||||
.unbounded_send(ToServiceCommand::WriteNotification(who, protocol, message));
|
||||
}
|
||||
|
||||
/// Set handshake for the notification protocol.
|
||||
pub fn set_notification_handshake(&self, protocol: ProtocolName, handshake: Vec<u8>) {
|
||||
let _ = self
|
||||
.tx
|
||||
.unbounded_send(ToServiceCommand::SetNotificationHandshake(protocol, handshake));
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkServiceProvider {
|
||||
@@ -114,6 +134,10 @@ impl NetworkServiceProvider {
|
||||
service.report_peer(peer, reputation_change),
|
||||
ToServiceCommand::StartRequest(peer, protocol, request, tx, connect) =>
|
||||
service.start_request(peer, protocol, request, tx, connect),
|
||||
ToServiceCommand::WriteNotification(peer, protocol, message) =>
|
||||
service.write_notification(peer, protocol, message),
|
||||
ToServiceCommand::SetNotificationHandshake(protocol, handshake) =>
|
||||
service.set_notification_handshake(protocol, handshake),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 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/>.
|
||||
|
||||
use crate::{service::network::NetworkServiceProvider, ChainSync, ForkTarget};
|
||||
|
||||
use libp2p::PeerId;
|
||||
use sc_network_common::{
|
||||
config::ProtocolId,
|
||||
protocol::{
|
||||
role::{Role, Roles},
|
||||
ProtocolName,
|
||||
},
|
||||
service::NetworkSyncForkRequest,
|
||||
sync::ChainSync as ChainSyncT,
|
||||
};
|
||||
use sp_consensus::block_validation::DefaultBlockAnnounceValidator;
|
||||
use sp_core::H256;
|
||||
use std::{sync::Arc, task::Poll};
|
||||
use substrate_test_runtime_client::{TestClientBuilder, TestClientBuilderExt as _};
|
||||
|
||||
// verify that the fork target map is empty, then submit a new sync fork request,
|
||||
// poll `ChainSync` and verify that a new sync fork request has been registered
|
||||
#[tokio::test]
|
||||
async fn delegate_to_chainsync() {
|
||||
let import_queue = Box::new(sc_consensus::import_queue::mock::MockImportQueueHandle::new());
|
||||
let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new();
|
||||
let (mut chain_sync, chain_sync_service, _) = ChainSync::new(
|
||||
sc_network_common::sync::SyncMode::Full,
|
||||
Arc::new(TestClientBuilder::with_default_backend().build_with_longest_chain().0),
|
||||
ProtocolId::from("test-protocol-name"),
|
||||
&Some(String::from("test-fork-id")),
|
||||
Roles::from(&Role::Full),
|
||||
Box::new(DefaultBlockAnnounceValidator),
|
||||
1u32,
|
||||
None,
|
||||
None,
|
||||
chain_sync_network_handle,
|
||||
import_queue,
|
||||
ProtocolName::from("block-request"),
|
||||
ProtocolName::from("state-request"),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let hash = H256::random();
|
||||
let in_number = 1337u64;
|
||||
let peers = (0..3).map(|_| PeerId::random()).collect::<Vec<_>>();
|
||||
|
||||
assert!(chain_sync.fork_targets.is_empty());
|
||||
chain_sync_service.set_sync_fork_request(peers, hash, in_number);
|
||||
|
||||
futures::future::poll_fn(|cx| {
|
||||
let _ = chain_sync.poll(cx);
|
||||
Poll::Ready(())
|
||||
})
|
||||
.await;
|
||||
|
||||
if let Some(ForkTarget { number, .. }) = chain_sync.fork_targets.get(&hash) {
|
||||
assert_eq!(number, &in_number);
|
||||
} else {
|
||||
panic!("expected to contain `ForkTarget`");
|
||||
}
|
||||
}
|
||||
@@ -46,24 +46,24 @@ use sc_consensus::{
|
||||
ForkChoiceStrategy, ImportQueue, ImportResult, JustificationImport, JustificationSyncLink,
|
||||
LongestChain, Verifier,
|
||||
};
|
||||
use sc_network::{
|
||||
config::{NetworkConfiguration, RequestResponseConfig, Role, SyncMode},
|
||||
Multiaddr, NetworkService, NetworkWorker,
|
||||
};
|
||||
use sc_network::{Multiaddr, NetworkService, NetworkWorker};
|
||||
use sc_network_common::{
|
||||
config::{
|
||||
MultiaddrWithPeerId, NonDefaultSetConfig, NonReservedPeerMode, ProtocolId, TransportConfig,
|
||||
MultiaddrWithPeerId, NetworkConfiguration, NonDefaultSetConfig, NonReservedPeerMode,
|
||||
ProtocolId, RequestResponseConfig, Role, SyncMode, TransportConfig,
|
||||
},
|
||||
protocol::{role::Roles, ProtocolName},
|
||||
service::{NetworkBlock, NetworkStateInfo, NetworkSyncForkRequest},
|
||||
service::{NetworkBlock, NetworkEventStream, NetworkStateInfo, NetworkSyncForkRequest},
|
||||
sync::warp::{
|
||||
AuthorityList, EncodedProof, SetId, VerificationResult, WarpSyncParams, WarpSyncProvider,
|
||||
},
|
||||
};
|
||||
use sc_network_light::light_client_requests::handler::LightClientRequestHandler;
|
||||
use sc_network_sync::{
|
||||
block_request_handler::BlockRequestHandler, service::network::NetworkServiceProvider,
|
||||
state_request_handler::StateRequestHandler, warp_request_handler, ChainSync,
|
||||
block_request_handler::BlockRequestHandler,
|
||||
service::{chain_sync::SyncingService, network::NetworkServiceProvider},
|
||||
state_request_handler::StateRequestHandler,
|
||||
warp_request_handler,
|
||||
};
|
||||
use sc_service::client::Client;
|
||||
use sp_blockchain::{
|
||||
@@ -241,7 +241,8 @@ pub struct Peer<D, BlockImport> {
|
||||
block_import: BlockImportAdapter<BlockImport>,
|
||||
select_chain: Option<LongestChain<substrate_test_runtime_client::Backend, Block>>,
|
||||
backend: Option<Arc<substrate_test_runtime_client::Backend>>,
|
||||
network: NetworkWorker<Block, <Block as BlockT>::Hash, PeersFullClient>,
|
||||
network: NetworkWorker<Block, <Block as BlockT>::Hash>,
|
||||
sync_service: Arc<SyncingService<Block>>,
|
||||
imported_blocks_stream: Pin<Box<dyn Stream<Item = BlockImportNotification<Block>> + Send>>,
|
||||
finality_notification_stream: Pin<Box<dyn Stream<Item = FinalityNotification<Block>> + Send>>,
|
||||
listen_addr: Multiaddr,
|
||||
@@ -259,7 +260,7 @@ where
|
||||
|
||||
/// Returns true if we're major syncing.
|
||||
pub fn is_major_syncing(&self) -> bool {
|
||||
self.network.service().is_major_syncing()
|
||||
self.sync_service.is_major_syncing()
|
||||
}
|
||||
|
||||
// Returns a clone of the local SelectChain, only available on full nodes
|
||||
@@ -275,23 +276,23 @@ where
|
||||
}
|
||||
|
||||
/// Returns the number of downloaded blocks.
|
||||
pub fn num_downloaded_blocks(&self) -> usize {
|
||||
self.network.num_downloaded_blocks()
|
||||
pub async fn num_downloaded_blocks(&self) -> usize {
|
||||
self.sync_service.num_downloaded_blocks().await.unwrap()
|
||||
}
|
||||
|
||||
/// Returns true if we have no peer.
|
||||
pub fn is_offline(&self) -> bool {
|
||||
self.num_peers() == 0
|
||||
self.sync_service.is_offline()
|
||||
}
|
||||
|
||||
/// Request a justification for the given block.
|
||||
pub fn request_justification(&self, hash: &<Block as BlockT>::Hash, number: NumberFor<Block>) {
|
||||
self.network.service().request_justification(hash, number);
|
||||
self.sync_service.request_justification(hash, number);
|
||||
}
|
||||
|
||||
/// Announces an important block on the network.
|
||||
pub fn announce_block(&self, hash: <Block as BlockT>::Hash, data: Option<Vec<u8>>) {
|
||||
self.network.service().announce_block(hash, data);
|
||||
self.sync_service.announce_block(hash, data);
|
||||
}
|
||||
|
||||
/// Request explicit fork sync.
|
||||
@@ -301,7 +302,7 @@ where
|
||||
hash: <Block as BlockT>::Hash,
|
||||
number: NumberFor<Block>,
|
||||
) {
|
||||
self.network.service().set_sync_fork_request(peers, hash, number);
|
||||
self.sync_service.set_sync_fork_request(peers, hash, number);
|
||||
}
|
||||
|
||||
/// Add blocks to the peer -- edit the block before adding
|
||||
@@ -402,14 +403,14 @@ where
|
||||
futures::executor::block_on(self.block_import.import_block(import_block, cache))
|
||||
.expect("block_import failed");
|
||||
if announce_block {
|
||||
self.network.service().announce_block(hash, None);
|
||||
self.sync_service.announce_block(hash, None);
|
||||
}
|
||||
hashes.push(hash);
|
||||
at = hash;
|
||||
}
|
||||
|
||||
if inform_sync_about_new_best_block {
|
||||
self.network.new_best_block_imported(
|
||||
self.sync_service.new_best_block_imported(
|
||||
at,
|
||||
*full_client.header(at).ok().flatten().unwrap().number(),
|
||||
);
|
||||
@@ -525,8 +526,12 @@ where
|
||||
self.network.service()
|
||||
}
|
||||
|
||||
pub fn sync_service(&self) -> &Arc<SyncingService<Block>> {
|
||||
&self.sync_service
|
||||
}
|
||||
|
||||
/// Get a reference to the network worker.
|
||||
pub fn network(&self) -> &NetworkWorker<Block, <Block as BlockT>::Hash, PeersFullClient> {
|
||||
pub fn network(&self) -> &NetworkWorker<Block, <Block as BlockT>::Hash> {
|
||||
&self.network
|
||||
}
|
||||
|
||||
@@ -728,13 +733,13 @@ pub struct FullPeerConfig {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait TestNetFactory: Default + Sized
|
||||
pub trait TestNetFactory: Default + Sized + Send
|
||||
where
|
||||
<Self::BlockImport as BlockImport<Block>>::Transaction: Send,
|
||||
{
|
||||
type Verifier: 'static + Verifier<Block>;
|
||||
type BlockImport: BlockImport<Block, Error = ConsensusError> + Clone + Send + Sync + 'static;
|
||||
type PeerData: Default;
|
||||
type PeerData: Default + Send;
|
||||
|
||||
/// This one needs to be implemented!
|
||||
fn make_verifier(&self, client: PeersClient, peer_data: &Self::PeerData) -> Self::Verifier;
|
||||
@@ -742,6 +747,7 @@ where
|
||||
/// Get reference to peer.
|
||||
fn peer(&mut self, i: usize) -> &mut Peer<Self::PeerData, Self::BlockImport>;
|
||||
fn peers(&self) -> &Vec<Peer<Self::PeerData, Self::BlockImport>>;
|
||||
fn peers_mut(&mut self) -> &mut Vec<Peer<Self::PeerData, Self::BlockImport>>;
|
||||
fn mut_peers<F: FnOnce(&mut Vec<Peer<Self::PeerData, Self::BlockImport>>)>(
|
||||
&mut self,
|
||||
closure: F,
|
||||
@@ -900,31 +906,25 @@ where
|
||||
let (chain_sync_network_provider, chain_sync_network_handle) =
|
||||
NetworkServiceProvider::new();
|
||||
|
||||
let (chain_sync, chain_sync_service, block_announce_config) = ChainSync::new(
|
||||
match network_config.sync_mode {
|
||||
SyncMode::Full => sc_network_common::sync::SyncMode::Full,
|
||||
SyncMode::Fast { skip_proofs, storage_chain_mode } =>
|
||||
sc_network_common::sync::SyncMode::LightState {
|
||||
skip_proofs,
|
||||
storage_chain_mode,
|
||||
},
|
||||
SyncMode::Warp => sc_network_common::sync::SyncMode::Warp,
|
||||
},
|
||||
client.clone(),
|
||||
protocol_id.clone(),
|
||||
&fork_id,
|
||||
Roles::from(if config.is_authority { &Role::Authority } else { &Role::Full }),
|
||||
block_announce_validator,
|
||||
network_config.max_parallel_downloads,
|
||||
Some(warp_sync_params),
|
||||
None,
|
||||
chain_sync_network_handle,
|
||||
import_queue.service(),
|
||||
block_request_protocol_config.name.clone(),
|
||||
state_request_protocol_config.name.clone(),
|
||||
Some(warp_protocol_config.name.clone()),
|
||||
)
|
||||
.unwrap();
|
||||
let (engine, sync_service, block_announce_config) =
|
||||
sc_network_sync::engine::SyncingEngine::new(
|
||||
Roles::from(if config.is_authority { &Role::Authority } else { &Role::Full }),
|
||||
client.clone(),
|
||||
None,
|
||||
&network_config,
|
||||
protocol_id.clone(),
|
||||
&fork_id,
|
||||
block_announce_validator,
|
||||
Some(warp_sync_params),
|
||||
chain_sync_network_handle,
|
||||
import_queue.service(),
|
||||
block_request_protocol_config.name.clone(),
|
||||
state_request_protocol_config.name.clone(),
|
||||
Some(warp_protocol_config.name.clone()),
|
||||
)
|
||||
.unwrap();
|
||||
let sync_service_import_queue = Box::new(sync_service.clone());
|
||||
let sync_service = Arc::new(sync_service.clone());
|
||||
|
||||
let network = NetworkWorker::new(sc_network::config::Params {
|
||||
role: if config.is_authority { Role::Authority } else { Role::Full },
|
||||
@@ -935,8 +935,6 @@ where
|
||||
chain: client.clone(),
|
||||
protocol_id,
|
||||
fork_id,
|
||||
chain_sync: Box::new(chain_sync),
|
||||
chain_sync_service: Box::new(chain_sync_service.clone()),
|
||||
metrics_registry: None,
|
||||
block_announce_config,
|
||||
request_response_protocol_configs: [
|
||||
@@ -955,8 +953,14 @@ where
|
||||
tokio::spawn(async move {
|
||||
chain_sync_network_provider.run(service).await;
|
||||
});
|
||||
|
||||
tokio::spawn(async move {
|
||||
import_queue.run(Box::new(chain_sync_service)).await;
|
||||
import_queue.run(sync_service_import_queue).await;
|
||||
});
|
||||
|
||||
let service = network.service().clone();
|
||||
tokio::spawn(async move {
|
||||
engine.run(service.event_stream("syncing")).await;
|
||||
});
|
||||
|
||||
self.mut_peers(move |peers| {
|
||||
@@ -979,6 +983,7 @@ where
|
||||
block_import,
|
||||
verifier,
|
||||
network,
|
||||
sync_service,
|
||||
listen_addr,
|
||||
});
|
||||
});
|
||||
@@ -989,48 +994,6 @@ where
|
||||
tokio::spawn(f);
|
||||
}
|
||||
|
||||
/// Polls the testnet until all nodes are in sync.
|
||||
///
|
||||
/// Must be executed in a task context.
|
||||
fn poll_until_sync(&mut self, cx: &mut FutureContext) -> Poll<()> {
|
||||
self.poll(cx);
|
||||
|
||||
// Return `NotReady` if there's a mismatch in the highest block number.
|
||||
let mut highest = None;
|
||||
for peer in self.peers().iter() {
|
||||
if peer.is_major_syncing() || peer.network.num_queued_blocks() != 0 {
|
||||
return Poll::Pending
|
||||
}
|
||||
if peer.network.num_sync_requests() != 0 {
|
||||
return Poll::Pending
|
||||
}
|
||||
match (highest, peer.client.info().best_hash) {
|
||||
(None, b) => highest = Some(b),
|
||||
(Some(ref a), ref b) if a == b => {},
|
||||
(Some(_), _) => return Poll::Pending,
|
||||
}
|
||||
}
|
||||
Poll::Ready(())
|
||||
}
|
||||
|
||||
/// Polls the testnet until theres' no activiy of any kind.
|
||||
///
|
||||
/// Must be executed in a task context.
|
||||
fn poll_until_idle(&mut self, cx: &mut FutureContext) -> Poll<()> {
|
||||
self.poll(cx);
|
||||
|
||||
for peer in self.peers().iter() {
|
||||
if peer.is_major_syncing() || peer.network.num_queued_blocks() != 0 {
|
||||
return Poll::Pending
|
||||
}
|
||||
if peer.network.num_sync_requests() != 0 {
|
||||
return Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
Poll::Ready(())
|
||||
}
|
||||
|
||||
/// Polls the testnet until all peers are connected to each other.
|
||||
///
|
||||
/// Must be executed in a task context.
|
||||
@@ -1045,15 +1008,61 @@ where
|
||||
Poll::Pending
|
||||
}
|
||||
|
||||
/// Run the network until we are sync'ed.
|
||||
async fn is_in_sync(&mut self) -> bool {
|
||||
let mut highest = None;
|
||||
let peers = self.peers_mut();
|
||||
|
||||
for peer in peers {
|
||||
if peer.sync_service.is_major_syncing() ||
|
||||
peer.sync_service.num_queued_blocks().await.unwrap() != 0
|
||||
{
|
||||
return false
|
||||
}
|
||||
if peer.sync_service.num_sync_requests().await.unwrap() != 0 {
|
||||
return false
|
||||
}
|
||||
match (highest, peer.client.info().best_hash) {
|
||||
(None, b) => highest = Some(b),
|
||||
(Some(ref a), ref b) if a == b => {},
|
||||
(Some(_), _) => return false,
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
async fn is_idle(&mut self) -> bool {
|
||||
let peers = self.peers_mut();
|
||||
for peer in peers {
|
||||
if peer.sync_service.num_queued_blocks().await.unwrap() != 0 {
|
||||
return false
|
||||
}
|
||||
if peer.sync_service.num_sync_requests().await.unwrap() != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Blocks the current thread until we are sync'ed.
|
||||
/// Wait until we are sync'ed.
|
||||
///
|
||||
/// Calls `poll_until_sync` repeatedly.
|
||||
/// (If we've not synced within 10 mins then panic rather than hang.)
|
||||
async fn run_until_sync(&mut self) {
|
||||
timeout(
|
||||
Duration::from_secs(10 * 60),
|
||||
futures::future::poll_fn::<(), _>(|cx| self.poll_until_sync(cx)),
|
||||
)
|
||||
timeout(Duration::from_secs(10 * 60), async {
|
||||
loop {
|
||||
futures::future::poll_fn::<(), _>(|cx| {
|
||||
self.poll(cx);
|
||||
Poll::Ready(())
|
||||
})
|
||||
.await;
|
||||
|
||||
if self.is_in_sync().await {
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("sync didn't happen within 10 mins");
|
||||
}
|
||||
@@ -1062,7 +1071,17 @@ where
|
||||
///
|
||||
/// Calls `poll_until_idle` repeatedly with the runtime passed as parameter.
|
||||
async fn run_until_idle(&mut self) {
|
||||
futures::future::poll_fn::<(), _>(|cx| self.poll_until_idle(cx)).await;
|
||||
loop {
|
||||
futures::future::poll_fn::<(), _>(|cx| {
|
||||
self.poll(cx);
|
||||
Poll::Ready(())
|
||||
})
|
||||
.await;
|
||||
|
||||
if self.is_idle().await {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the network until all peers are connected to each other.
|
||||
@@ -1095,14 +1114,14 @@ where
|
||||
while let Poll::Ready(Some(notification)) =
|
||||
peer.imported_blocks_stream.as_mut().poll_next(cx)
|
||||
{
|
||||
peer.network.service().announce_block(notification.hash, None);
|
||||
peer.sync_service.announce_block(notification.hash, None);
|
||||
}
|
||||
|
||||
// We poll `finality_notification_stream`.
|
||||
while let Poll::Ready(Some(notification)) =
|
||||
peer.finality_notification_stream.as_mut().poll_next(cx)
|
||||
{
|
||||
peer.network.on_block_finalized(notification.hash, notification.header);
|
||||
peer.sync_service.on_block_finalized(notification.hash, notification.header);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1142,6 +1161,10 @@ impl TestNetFactory for TestNet {
|
||||
&self.peers
|
||||
}
|
||||
|
||||
fn peers_mut(&mut self) -> &mut Vec<Peer<(), Self::BlockImport>> {
|
||||
&mut self.peers
|
||||
}
|
||||
|
||||
fn mut_peers<F: FnOnce(&mut Vec<Peer<(), Self::BlockImport>>)>(&mut self, closure: F) {
|
||||
closure(&mut self.peers);
|
||||
}
|
||||
@@ -1189,6 +1212,10 @@ impl TestNetFactory for JustificationTestNet {
|
||||
self.0.peers()
|
||||
}
|
||||
|
||||
fn peers_mut(&mut self) -> &mut Vec<Peer<Self::PeerData, Self::BlockImport>> {
|
||||
self.0.peers_mut()
|
||||
}
|
||||
|
||||
fn mut_peers<F: FnOnce(&mut Vec<Peer<Self::PeerData, Self::BlockImport>>)>(
|
||||
&mut self,
|
||||
closure: F,
|
||||
|
||||
@@ -652,12 +652,12 @@ async fn imports_stale_once() {
|
||||
// check that NEW block is imported from announce message
|
||||
let new_hash = net.peer(0).push_blocks(1, false).pop().unwrap();
|
||||
import_with_announce(&mut net, new_hash).await;
|
||||
assert_eq!(net.peer(1).num_downloaded_blocks(), 1);
|
||||
assert_eq!(net.peer(1).num_downloaded_blocks().await, 1);
|
||||
|
||||
// check that KNOWN STALE block is imported from announce message
|
||||
let known_stale_hash = net.peer(0).push_blocks_at(BlockId::Number(0), 1, true).pop().unwrap();
|
||||
import_with_announce(&mut net, known_stale_hash).await;
|
||||
assert_eq!(net.peer(1).num_downloaded_blocks(), 2);
|
||||
assert_eq!(net.peer(1).num_downloaded_blocks().await, 2);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
@@ -820,7 +820,7 @@ async fn sync_to_tip_requires_that_sync_protocol_is_informed_about_best_block()
|
||||
assert!(!net.peer(1).has_block(block_hash));
|
||||
|
||||
// Make sync protocol aware of the best block
|
||||
net.peer(0).network_service().new_best_block_imported(block_hash, 3);
|
||||
net.peer(0).sync_service().new_best_block_imported(block_hash, 3);
|
||||
net.run_until_idle().await;
|
||||
|
||||
// Connect another node that should now sync to the tip
|
||||
@@ -865,8 +865,8 @@ async fn sync_to_tip_when_we_sync_together_with_multiple_peers() {
|
||||
|
||||
assert!(!net.peer(2).has_block(block_hash));
|
||||
|
||||
net.peer(0).network_service().new_best_block_imported(block_hash, 10_000);
|
||||
net.peer(0).network_service().announce_block(block_hash, None);
|
||||
net.peer(0).sync_service().new_best_block_imported(block_hash, 10_000);
|
||||
net.peer(0).sync_service().announce_block(block_hash, None);
|
||||
|
||||
while !net.peer(2).has_block(block_hash) && !net.peer(1).has_block(block_hash) {
|
||||
net.run_until_idle().await;
|
||||
@@ -1045,14 +1045,17 @@ async fn syncs_all_forks_from_single_peer() {
|
||||
let branch1 = net.peer(0).push_blocks_at(BlockId::Number(10), 2, true).pop().unwrap();
|
||||
|
||||
// Wait till peer 1 starts downloading
|
||||
futures::future::poll_fn::<(), _>(|cx| {
|
||||
net.poll(cx);
|
||||
if net.peer(1).network().best_seen_block() != Some(12) {
|
||||
return Poll::Pending
|
||||
loop {
|
||||
futures::future::poll_fn::<(), _>(|cx| {
|
||||
net.poll(cx);
|
||||
Poll::Ready(())
|
||||
})
|
||||
.await;
|
||||
|
||||
if net.peer(1).sync_service().best_seen_block().await.unwrap() == Some(12) {
|
||||
break
|
||||
}
|
||||
Poll::Ready(())
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
// Peer 0 produces and announces another fork
|
||||
let branch2 = net.peer(0).push_blocks_at(BlockId::Number(10), 2, false).pop().unwrap();
|
||||
|
||||
@@ -37,6 +37,7 @@ use sc_network_common::{
|
||||
error,
|
||||
protocol::{event::Event, role::ObservedRole, ProtocolName},
|
||||
service::{NetworkEventStream, NetworkNotification, NetworkPeers},
|
||||
sync::{SyncEvent, SyncEventStream},
|
||||
utils::{interval, LruHashSet},
|
||||
ExHashT,
|
||||
};
|
||||
@@ -161,14 +162,17 @@ impl TransactionsHandlerPrototype {
|
||||
pub fn build<
|
||||
B: BlockT + 'static,
|
||||
H: ExHashT,
|
||||
S: NetworkPeers + NetworkEventStream + NetworkNotification + sp_consensus::SyncOracle,
|
||||
N: NetworkPeers + NetworkEventStream + NetworkNotification,
|
||||
S: SyncEventStream + sp_consensus::SyncOracle,
|
||||
>(
|
||||
self,
|
||||
service: S,
|
||||
network: N,
|
||||
sync: S,
|
||||
transaction_pool: Arc<dyn TransactionPool<H, B>>,
|
||||
metrics_registry: Option<&Registry>,
|
||||
) -> error::Result<(TransactionsHandler<B, H, S>, TransactionsHandlerController<H>)> {
|
||||
let event_stream = service.event_stream("transactions-handler");
|
||||
) -> error::Result<(TransactionsHandler<B, H, N, S>, TransactionsHandlerController<H>)> {
|
||||
let net_event_stream = network.event_stream("transactions-handler-net");
|
||||
let sync_event_stream = sync.event_stream("transactions-handler-sync");
|
||||
let (to_handler, from_controller) = tracing_unbounded("mpsc_transactions_handler", 100_000);
|
||||
|
||||
let handler = TransactionsHandler {
|
||||
@@ -178,8 +182,10 @@ impl TransactionsHandlerPrototype {
|
||||
.fuse(),
|
||||
pending_transactions: FuturesUnordered::new(),
|
||||
pending_transactions_peers: HashMap::new(),
|
||||
service,
|
||||
event_stream: event_stream.fuse(),
|
||||
network,
|
||||
sync,
|
||||
net_event_stream: net_event_stream.fuse(),
|
||||
sync_event_stream: sync_event_stream.fuse(),
|
||||
peers: HashMap::new(),
|
||||
transaction_pool,
|
||||
from_controller,
|
||||
@@ -228,7 +234,8 @@ enum ToHandler<H: ExHashT> {
|
||||
pub struct TransactionsHandler<
|
||||
B: BlockT + 'static,
|
||||
H: ExHashT,
|
||||
S: NetworkPeers + NetworkEventStream + NetworkNotification + sp_consensus::SyncOracle,
|
||||
N: NetworkPeers + NetworkEventStream + NetworkNotification,
|
||||
S: SyncEventStream + sp_consensus::SyncOracle,
|
||||
> {
|
||||
protocol_name: ProtocolName,
|
||||
/// Interval at which we call `propagate_transactions`.
|
||||
@@ -241,9 +248,13 @@ pub struct TransactionsHandler<
|
||||
/// multiple times concurrently.
|
||||
pending_transactions_peers: HashMap<H, Vec<PeerId>>,
|
||||
/// Network service to use to send messages and manage peers.
|
||||
service: S,
|
||||
network: N,
|
||||
/// Syncing service.
|
||||
sync: S,
|
||||
/// Stream of networking events.
|
||||
event_stream: stream::Fuse<Pin<Box<dyn Stream<Item = Event> + Send>>>,
|
||||
net_event_stream: stream::Fuse<Pin<Box<dyn Stream<Item = Event> + Send>>>,
|
||||
/// Receiver for syncing-related events.
|
||||
sync_event_stream: stream::Fuse<Pin<Box<dyn Stream<Item = SyncEvent> + Send>>>,
|
||||
// All connected peers
|
||||
peers: HashMap<PeerId, Peer<H>>,
|
||||
transaction_pool: Arc<dyn TransactionPool<H, B>>,
|
||||
@@ -260,11 +271,12 @@ struct Peer<H: ExHashT> {
|
||||
role: ObservedRole,
|
||||
}
|
||||
|
||||
impl<B, H, S> TransactionsHandler<B, H, S>
|
||||
impl<B, H, N, S> TransactionsHandler<B, H, N, S>
|
||||
where
|
||||
B: BlockT + 'static,
|
||||
H: ExHashT,
|
||||
S: NetworkPeers + NetworkEventStream + NetworkNotification + sp_consensus::SyncOracle,
|
||||
N: NetworkPeers + NetworkEventStream + NetworkNotification,
|
||||
S: SyncEventStream + sp_consensus::SyncOracle,
|
||||
{
|
||||
/// Turns the [`TransactionsHandler`] into a future that should run forever and not be
|
||||
/// interrupted.
|
||||
@@ -281,7 +293,7 @@ where
|
||||
warn!(target: "sub-libp2p", "Inconsistent state, no peers for pending transaction!");
|
||||
}
|
||||
},
|
||||
network_event = self.event_stream.next() => {
|
||||
network_event = self.net_event_stream.next() => {
|
||||
if let Some(network_event) = network_event {
|
||||
self.handle_network_event(network_event).await;
|
||||
} else {
|
||||
@@ -289,6 +301,14 @@ where
|
||||
return;
|
||||
}
|
||||
},
|
||||
sync_event = self.sync_event_stream.next() => {
|
||||
if let Some(sync_event) = sync_event {
|
||||
self.handle_sync_event(sync_event);
|
||||
} else {
|
||||
// Syncing has seemingly closed. Closing as well.
|
||||
return;
|
||||
}
|
||||
}
|
||||
message = self.from_controller.select_next_some() => {
|
||||
match message {
|
||||
ToHandler::PropagateTransaction(hash) => self.propagate_transaction(&hash),
|
||||
@@ -299,13 +319,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_network_event(&mut self, event: Event) {
|
||||
fn handle_sync_event(&mut self, event: SyncEvent) {
|
||||
match event {
|
||||
Event::Dht(_) => {},
|
||||
Event::SyncConnected { remote } => {
|
||||
SyncEvent::PeerConnected(remote) => {
|
||||
let addr = iter::once(multiaddr::Protocol::P2p(remote.into()))
|
||||
.collect::<multiaddr::Multiaddr>();
|
||||
let result = self.service.add_peers_to_reserved_set(
|
||||
let result = self.network.add_peers_to_reserved_set(
|
||||
self.protocol_name.clone(),
|
||||
iter::once(addr).collect(),
|
||||
);
|
||||
@@ -313,13 +332,18 @@ where
|
||||
log::error!(target: "sync", "Add reserved peer failed: {}", err);
|
||||
}
|
||||
},
|
||||
Event::SyncDisconnected { remote } => {
|
||||
self.service.remove_peers_from_reserved_set(
|
||||
SyncEvent::PeerDisconnected(remote) => {
|
||||
self.network.remove_peers_from_reserved_set(
|
||||
self.protocol_name.clone(),
|
||||
iter::once(remote).collect(),
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_network_event(&mut self, event: Event) {
|
||||
match event {
|
||||
Event::Dht(_) => {},
|
||||
Event::NotificationStreamOpened { remote, protocol, role, .. }
|
||||
if protocol == self.protocol_name =>
|
||||
{
|
||||
@@ -365,7 +389,7 @@ where
|
||||
/// Called when peer sends us new transactions
|
||||
fn on_transactions(&mut self, who: PeerId, transactions: Transactions<B::Extrinsic>) {
|
||||
// Accept transactions only when node is not major syncing
|
||||
if self.service.is_major_syncing() {
|
||||
if self.sync.is_major_syncing() {
|
||||
trace!(target: "sync", "{} Ignoring transactions while major syncing", who);
|
||||
return
|
||||
}
|
||||
@@ -385,7 +409,7 @@ where
|
||||
let hash = self.transaction_pool.hash_of(&t);
|
||||
peer.known_transactions.insert(hash.clone());
|
||||
|
||||
self.service.report_peer(who, rep::ANY_TRANSACTION);
|
||||
self.network.report_peer(who, rep::ANY_TRANSACTION);
|
||||
|
||||
match self.pending_transactions_peers.entry(hash.clone()) {
|
||||
Entry::Vacant(entry) => {
|
||||
@@ -406,9 +430,9 @@ where
|
||||
fn on_handle_transaction_import(&mut self, who: PeerId, import: TransactionImport) {
|
||||
match import {
|
||||
TransactionImport::KnownGood =>
|
||||
self.service.report_peer(who, rep::ANY_TRANSACTION_REFUND),
|
||||
TransactionImport::NewGood => self.service.report_peer(who, rep::GOOD_TRANSACTION),
|
||||
TransactionImport::Bad => self.service.report_peer(who, rep::BAD_TRANSACTION),
|
||||
self.network.report_peer(who, rep::ANY_TRANSACTION_REFUND),
|
||||
TransactionImport::NewGood => self.network.report_peer(who, rep::GOOD_TRANSACTION),
|
||||
TransactionImport::Bad => self.network.report_peer(who, rep::BAD_TRANSACTION),
|
||||
TransactionImport::None => {},
|
||||
}
|
||||
}
|
||||
@@ -416,7 +440,7 @@ where
|
||||
/// Propagate one transaction.
|
||||
pub fn propagate_transaction(&mut self, hash: &H) {
|
||||
// Accept transactions only when node is not major syncing
|
||||
if self.service.is_major_syncing() {
|
||||
if self.sync.is_major_syncing() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -453,7 +477,7 @@ where
|
||||
propagated_to.entry(hash).or_default().push(who.to_base58());
|
||||
}
|
||||
trace!(target: "sync", "Sending {} transactions to {}", to_send.len(), who);
|
||||
self.service
|
||||
self.network
|
||||
.write_notification(*who, self.protocol_name.clone(), to_send.encode());
|
||||
}
|
||||
}
|
||||
@@ -468,7 +492,7 @@ where
|
||||
/// Call when we must propagate ready transactions to peers.
|
||||
fn propagate_transactions(&mut self) {
|
||||
// Accept transactions only when node is not major syncing
|
||||
if self.service.is_major_syncing() {
|
||||
if self.sync.is_major_syncing() {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user