use std::{borrow::Cow, collections::{HashMap, HashSet}, fs, marker::PhantomData, io, path::Path, str};
use std::sync::{Arc, atomic::{AtomicBool, AtomicUsize, Ordering}};
use std::pin::Pin;
use std::task::Poll;
use sp_consensus::import_queue::{ImportQueue, Link};
use sp_consensus::import_queue::{BlockImportResult, BlockImportError};
use futures::{prelude::*, channel::mpsc};
use log::{warn, error, info, trace};
use libp2p::{PeerId, Multiaddr, kad::record};
use libp2p::swarm::{NetworkBehaviour, SwarmBuilder, SwarmEvent};
use parking_lot::Mutex;
use sc_peerset::PeersetHandle;
use sp_runtime::{traits::{Block as BlockT, NumberFor}, ConsensusEngineId};
use prometheus_endpoint::{Registry, Counter, CounterVec, Gauge, GaugeVec, Opts, U64, register, PrometheusError};
use crate::{behaviour::{Behaviour, BehaviourOut}, config::{parse_str_addr, parse_addr}};
use crate::{transport, config::NonReservedPeerMode, ReputationChange};
use crate::config::{Params, TransportConfig};
use crate::error::Error;
use crate::network_state::{NetworkState, NotConnectedPeer as NetworkStateNotConnectedPeer, Peer as NetworkStatePeer};
use crate::protocol::{self, Protocol, PeerInfo};
use crate::protocol::{event::Event, light_dispatch::{AlwaysBadChecker, RequestData}};
use crate::protocol::sync::SyncState;
pub trait ExHashT: std::hash::Hash + Eq + std::fmt::Debug + Clone + Send + Sync + 'static {}
impl<T> ExHashT for T where
T: std::hash::Hash + Eq + std::fmt::Debug + Clone + Send + Sync + 'static
{}
pub trait TransactionPool<H: ExHashT, B: BlockT>: Send + Sync {
fn transactions(&self) -> Vec<(H, B::Extrinsic)>;
fn hash_of(&self, transaction: &B::Extrinsic) -> H;
fn import(
&self,
report_handle: ReportHandle,
who: PeerId,
reputation_change_good: ReputationChange,
reputation_change_bad: ReputationChange,
transaction: B::Extrinsic,
);
fn on_broadcasted(&self, propagations: HashMap<H, Vec<String>>);
fn transaction(&self, hash: &H) -> Option<B::Extrinsic>;
}
pub struct EmptyTransactionPool;
impl<H: ExHashT + Default, B: BlockT> TransactionPool<H, B> for EmptyTransactionPool {
fn transactions(&self) -> Vec<(H, B::Extrinsic)> {
Vec::new()
}
fn hash_of(&self, _transaction: &B::Extrinsic) -> H {
Default::default()
}
fn import(
&self,
_report_handle: ReportHandle,
_who: PeerId,
_rep_change_good: ReputationChange,
_rep_change_bad: ReputationChange,
_transaction: B::Extrinsic
) {}
fn on_broadcasted(&self, _: HashMap<H, Vec<String>>) {}
fn transaction(&self, _h: &H) -> Option<B::Extrinsic> { None }
}
#[derive(Clone)]
pub struct ReportHandle {
inner: PeersetHandle,
}
impl From<PeersetHandle> for ReportHandle {
fn from(peerset_handle: PeersetHandle) -> Self {
ReportHandle { inner: peerset_handle }
}
}
impl ReportHandle {
pub fn report_peer(&self, who: PeerId, cost_benefit: ReputationChange) {
self.inner.report_peer(who, cost_benefit);
}
}
pub struct NetworkService<B: BlockT + 'static, H: ExHashT> {
num_connected: Arc<AtomicUsize>,
external_addresses: Arc<Mutex<Vec<Multiaddr>>>,
is_major_syncing: Arc<AtomicBool>,
local_peer_id: PeerId,
bandwidth: Arc<transport::BandwidthSinks>,
peerset: PeersetHandle,
to_worker: mpsc::UnboundedSender<ServiceToWorkerMsg<B, H>>,
_marker: PhantomData<H>,
}
impl<B: BlockT + 'static, H: ExHashT> NetworkWorker<B, H> {
pub fn new(params: Params<B, H>) -> Result<NetworkWorker<B, H>, Error> {
let (to_worker, from_worker) = mpsc::unbounded();
if let Some(ref path) = params.network_config.net_config_path {
fs::create_dir_all(Path::new(path))?;
}
let mut known_addresses = Vec::new();
let mut bootnodes = Vec::new();
let mut reserved_nodes = Vec::new();
for bootnode in params.network_config.boot_nodes.iter() {
match parse_str_addr(bootnode) {
Ok((peer_id, addr)) => {
bootnodes.push(peer_id.clone());
known_addresses.push((peer_id, addr));
},
Err(_) => warn!(target: "sub-libp2p", "Not a valid bootnode address: {}", bootnode),
}
}
known_addresses.iter()
.try_for_each(|(peer_id, addr)|
if let Some(other) = known_addresses
.iter()
.find(|o| o.1 == *addr && o.0 != *peer_id)
{
Err(Error::DuplicateBootnode {
address: addr.clone(),
first_id: peer_id.clone(),
second_id: other.0.clone(),
})
} else {
Ok(())
}
)?;
for reserved in params.network_config.reserved_nodes.iter() {
if let Ok((peer_id, addr)) = parse_str_addr(reserved) {
reserved_nodes.push(peer_id.clone());
known_addresses.push((peer_id, addr));
} else {
warn!(target: "sub-libp2p", "Not a valid reserved node address: {}", reserved);
}
}
let peerset_config = sc_peerset::PeersetConfig {
in_peers: params.network_config.in_peers,
out_peers: params.network_config.out_peers,
bootnodes,
reserved_only: params.network_config.non_reserved_mode == NonReservedPeerMode::Deny,
reserved_nodes,
};
let local_identity = params.network_config.node_key.clone().into_keypair()?;
let local_public = local_identity.public();
let local_peer_id = local_public.clone().into_peer_id();
info!(target: "sub-libp2p", "Local node identity is: {}", local_peer_id.to_base58());
let checker = params.on_demand.as_ref()
.map(|od| od.checker().clone())
.unwrap_or(Arc::new(AlwaysBadChecker));
let num_connected = Arc::new(AtomicUsize::new(0));
let is_major_syncing = Arc::new(AtomicBool::new(false));
let (protocol, peerset_handle) = Protocol::new(
protocol::ProtocolConfig {
roles: params.roles,
max_parallel_downloads: params.network_config.max_parallel_downloads,
},
params.chain.clone(),
checker.clone(),
params.transaction_pool,
params.finality_proof_provider.clone(),
params.finality_proof_request_builder,
params.protocol_id.clone(),
peerset_config,
params.block_announce_validator,
params.metrics_registry.as_ref()
)?;
let (mut swarm, bandwidth): (Swarm::<B, H>, _) = {
let user_agent = format!(
"{} ({})",
params.network_config.client_version,
params.network_config.node_name
);
let block_requests = {
let config = protocol::block_requests::Config::new(¶ms.protocol_id);
protocol::BlockRequests::new(config, params.chain.clone())
};
let light_client_handler = {
let config = protocol::light_client_handler::Config::new(¶ms.protocol_id);
protocol::LightClientHandler::new(config, params.chain, checker, peerset_handle.clone())
};
let behaviour = futures::executor::block_on(Behaviour::new(
protocol,
user_agent,
local_public,
known_addresses,
match params.network_config.transport {
TransportConfig::MemoryOnly => false,
TransportConfig::Normal { enable_mdns, .. } => enable_mdns,
},
match params.network_config.transport {
TransportConfig::MemoryOnly => false,
TransportConfig::Normal { allow_private_ipv4, .. } => allow_private_ipv4,
},
u64::from(params.network_config.out_peers) + 15,
block_requests,
light_client_handler
));
let (transport, bandwidth) = {
let (config_mem, config_wasm, flowctrl) = match params.network_config.transport {
TransportConfig::MemoryOnly => (true, None, false),
TransportConfig::Normal { wasm_external_transport, use_yamux_flow_control, .. } =>
(false, wasm_external_transport, use_yamux_flow_control)
};
transport::build_transport(local_identity, config_mem, config_wasm, flowctrl)
};
let mut builder = SwarmBuilder::new(transport, behaviour, local_peer_id.clone());
if let Some(spawner) = params.executor {
builder = builder.executor_fn(spawner);
}
(builder.build(), bandwidth)
};
for addr in ¶ms.network_config.listen_addresses {
if let Err(err) = Swarm::<B, H>::listen_on(&mut swarm, addr.clone()) {
warn!(target: "sub-libp2p", "Can't listen on {} because: {:?}", addr, err)
}
}
for addr in ¶ms.network_config.public_addresses {
Swarm::<B, H>::add_external_address(&mut swarm, addr.clone());
}
let external_addresses = Arc::new(Mutex::new(Vec::new()));
let service = Arc::new(NetworkService {
bandwidth,
external_addresses: external_addresses.clone(),
num_connected: num_connected.clone(),
is_major_syncing: is_major_syncing.clone(),
peerset: peerset_handle,
local_peer_id,
to_worker: to_worker.clone(),
_marker: PhantomData,
});
Ok(NetworkWorker {
external_addresses,
num_connected,
is_major_syncing,
network_service: swarm,
service,
import_queue: params.import_queue,
from_worker,
light_client_rqs: params.on_demand.and_then(|od| od.extract_receiver()),
event_streams: Vec::new(),
metrics: match params.metrics_registry {
Some(registry) => Some(Metrics::register(®istry)?),
None => None
}
})
}
pub fn average_download_per_sec(&self) -> u64 {
self.service.bandwidth.average_download_per_sec()
}
pub fn average_upload_per_sec(&self) -> u64 {
self.service.bandwidth.average_upload_per_sec()
}
pub fn num_connected_peers(&self) -> usize {
self.network_service.user_protocol().num_connected_peers()
}
pub fn num_active_peers(&self) -> usize {
self.network_service.user_protocol().num_active_peers()
}
pub fn sync_state(&self) -> SyncState {
self.network_service.user_protocol().sync_state()
}
pub fn best_seen_block(&self) -> Option<NumberFor<B>> {
self.network_service.user_protocol().best_seen_block()
}
pub fn num_sync_peers(&self) -> u32 {
self.network_service.user_protocol().num_sync_peers()
}
pub fn num_queued_blocks(&self) -> u32 {
self.network_service.user_protocol().num_queued_blocks()
}
pub fn num_processed_blocks(&self) -> usize {
self.network_service.user_protocol().num_processed_blocks()
}
pub fn num_sync_requests(&self) -> usize {
self.network_service.user_protocol().num_sync_requests()
}
pub fn add_known_address(&mut self, peer_id: PeerId, addr: Multiaddr) {
self.network_service.add_known_address(peer_id, addr);
}
pub fn service(&self) -> &Arc<NetworkService<B, H>> {
&self.service
}
pub fn on_block_imported(&mut self, header: B::Header, data: Vec<u8>, is_best: bool) {
self.network_service.user_protocol_mut().on_block_imported(&header, data, is_best);
}
pub fn on_block_finalized(&mut self, hash: B::Hash, header: B::Header) {
self.network_service.user_protocol_mut().on_block_finalized(hash, &header);
}
pub fn network_state(&mut self) -> NetworkState {
let swarm = &mut self.network_service;
let open = swarm.user_protocol().open_peers().cloned().collect::<Vec<_>>();
let connected_peers = {
let swarm = &mut *swarm;
open.iter().filter_map(move |peer_id| {
let known_addresses = NetworkBehaviour::addresses_of_peer(&mut **swarm, peer_id)
.into_iter().collect();
let endpoint = if let Some(e) = swarm.node(peer_id).map(|i| i.endpoint()) {
e.clone().into()
} else {
error!(target: "sub-libp2p", "Found state inconsistency between custom protocol \
and debug information about {:?}", peer_id);
return None
};
Some((peer_id.to_base58(), NetworkStatePeer {
endpoint,
version_string: swarm.node(peer_id)
.and_then(|i| i.client_version().map(|s| s.to_owned())).clone(),
latest_ping_time: swarm.node(peer_id).and_then(|i| i.latest_ping()),
enabled: swarm.user_protocol().is_enabled(&peer_id),
open: swarm.user_protocol().is_open(&peer_id),
known_addresses,
}))
}).collect()
};
let not_connected_peers = {
let swarm = &mut *swarm;
let list = swarm.known_peers().filter(|p| open.iter().all(|n| n != *p))
.cloned().collect::<Vec<_>>();
list.into_iter().map(move |peer_id| {
(peer_id.to_base58(), NetworkStateNotConnectedPeer {
version_string: swarm.node(&peer_id)
.and_then(|i| i.client_version().map(|s| s.to_owned())).clone(),
latest_ping_time: swarm.node(&peer_id).and_then(|i| i.latest_ping()),
known_addresses: NetworkBehaviour::addresses_of_peer(&mut **swarm, &peer_id)
.into_iter().collect(),
})
}).collect()
};
NetworkState {
peer_id: Swarm::<B, H>::local_peer_id(&swarm).to_base58(),
listened_addresses: Swarm::<B, H>::listeners(&swarm).cloned().collect(),
external_addresses: Swarm::<B, H>::external_addresses(&swarm).cloned().collect(),
average_download_per_sec: self.service.bandwidth.average_download_per_sec(),
average_upload_per_sec: self.service.bandwidth.average_upload_per_sec(),
connected_peers,
not_connected_peers,
peerset: swarm.user_protocol_mut().peerset_debug_info(),
}
}
pub fn peers_debug_info(&mut self) -> Vec<(PeerId, PeerInfo<B>)> {
self.network_service.user_protocol_mut()
.peers_info()
.map(|(id, info)| (id.clone(), info.clone()))
.collect()
}
pub fn remove_reserved_peer(&self, peer: PeerId) {
self.service.remove_reserved_peer(peer);
}
pub fn add_reserved_peer(&self, peer: String) -> Result<(), String> {
self.service.add_reserved_peer(peer)
}
}
impl<B: BlockT + 'static, H: ExHashT> NetworkService<B, H> {
pub fn write_notification(&self, target: PeerId, engine_id: ConsensusEngineId, message: Vec<u8>) {
let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::WriteNotification {
target,
engine_id,
message,
});
}
pub fn event_stream(&self) -> impl Stream<Item = Event> {
let (tx, rx) = mpsc::unbounded();
let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::EventStream(tx));
rx
}
pub fn register_notifications_protocol(
&self,
engine_id: ConsensusEngineId,
protocol_name: impl Into<Cow<'static, [u8]>>,
) {
let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::RegisterNotifProtocol {
engine_id,
protocol_name: protocol_name.into(),
});
}
pub fn trigger_repropagate(&self) {
let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::PropagateExtrinsics);
}
pub fn propagate_extrinsic(&self, hash: H) {
let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::PropagateExtrinsic(hash));
}
pub fn announce_block(&self, hash: B::Hash, data: Vec<u8>) {
let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::AnnounceBlock(hash, data));
}
pub fn report_peer(&self, who: PeerId, cost_benefit: ReputationChange) {
self.peerset.report_peer(who, cost_benefit);
}
pub fn disconnect_peer(&self, who: PeerId) {
let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::DisconnectPeer(who));
}
pub fn request_justification(&self, hash: &B::Hash, number: NumberFor<B>) {
let _ = self
.to_worker
.unbounded_send(ServiceToWorkerMsg::RequestJustification(hash.clone(), number));
}
pub fn is_major_syncing(&self) -> bool {
self.is_major_syncing.load(Ordering::Relaxed)
}
pub fn get_value(&self, key: &record::Key) {
let _ = self
.to_worker
.unbounded_send(ServiceToWorkerMsg::GetValue(key.clone()));
}
pub fn put_value(&self, key: record::Key, value: Vec<u8>) {
let _ = self
.to_worker
.unbounded_send(ServiceToWorkerMsg::PutValue(key, value));
}
pub fn accept_unreserved_peers(&self) {
self.peerset.set_reserved_only(false);
}
pub fn deny_unreserved_peers(&self) {
self.peerset.set_reserved_only(true);
}
pub fn remove_reserved_peer(&self, peer: PeerId) {
self.peerset.remove_reserved_peer(peer);
}
pub fn add_reserved_peer(&self, peer: String) -> Result<(), String> {
let (peer_id, addr) = parse_str_addr(&peer).map_err(|e| format!("{:?}", e))?;
self.peerset.add_reserved_peer(peer_id.clone());
let _ = self
.to_worker
.unbounded_send(ServiceToWorkerMsg::AddKnownAddress(peer_id, addr));
Ok(())
}
pub fn set_sync_fork_request(&self, peers: Vec<PeerId>, hash: B::Hash, number: NumberFor<B>) {
let _ = self
.to_worker
.unbounded_send(ServiceToWorkerMsg::SyncFork(peers, hash, number));
}
pub fn set_priority_group(&self, group_id: String, peers: HashSet<Multiaddr>) -> Result<(), String> {
let peers = peers.into_iter().map(|p| {
parse_addr(p).map_err(|e| format!("{:?}", e))
}).collect::<Result<Vec<(PeerId, Multiaddr)>, String>>()?;
let peer_ids = peers.iter().map(|(peer_id, _addr)| peer_id.clone()).collect();
self.peerset.set_priority_group(group_id, peer_ids);
for (peer_id, addr) in peers.into_iter() {
let _ = self
.to_worker
.unbounded_send(ServiceToWorkerMsg::AddKnownAddress(peer_id, addr));
}
Ok(())
}
pub fn num_connected(&self) -> usize {
self.num_connected.load(Ordering::Relaxed)
}
}
impl<B: BlockT + 'static, H: ExHashT> sp_consensus::SyncOracle
for NetworkService<B, H>
{
fn is_major_syncing(&mut self) -> bool {
NetworkService::is_major_syncing(self)
}
fn is_offline(&mut self) -> bool {
self.num_connected.load(Ordering::Relaxed) == 0
}
}
impl<'a, B: BlockT + 'static, H: ExHashT> sp_consensus::SyncOracle
for &'a NetworkService<B, H>
{
fn is_major_syncing(&mut self) -> bool {
NetworkService::is_major_syncing(self)
}
fn is_offline(&mut self) -> bool {
self.num_connected.load(Ordering::Relaxed) == 0
}
}
pub trait NetworkStateInfo {
fn external_addresses(&self) -> Vec<Multiaddr>;
fn local_peer_id(&self) -> PeerId;
}
impl<B, H> NetworkStateInfo for NetworkService<B, H>
where
B: sp_runtime::traits::Block,
H: ExHashT,
{
fn external_addresses(&self) -> Vec<Multiaddr> {
self.external_addresses.lock().clone()
}
fn local_peer_id(&self) -> PeerId {
self.local_peer_id.clone()
}
}
enum ServiceToWorkerMsg<B: BlockT, H: ExHashT> {
PropagateExtrinsic(H),
PropagateExtrinsics,
RequestJustification(B::Hash, NumberFor<B>),
AnnounceBlock(B::Hash, Vec<u8>),
GetValue(record::Key),
PutValue(record::Key, Vec<u8>),
AddKnownAddress(PeerId, Multiaddr),
SyncFork(Vec<PeerId>, B::Hash, NumberFor<B>),
EventStream(mpsc::UnboundedSender<Event>),
WriteNotification {
message: Vec<u8>,
engine_id: ConsensusEngineId,
target: PeerId,
},
RegisterNotifProtocol {
engine_id: ConsensusEngineId,
protocol_name: Cow<'static, [u8]>,
},
DisconnectPeer(PeerId),
}
#[must_use = "The NetworkWorker must be polled in order for the network to work"]
pub struct NetworkWorker<B: BlockT + 'static, H: ExHashT> {
external_addresses: Arc<Mutex<Vec<Multiaddr>>>,
num_connected: Arc<AtomicUsize>,
is_major_syncing: Arc<AtomicBool>,
service: Arc<NetworkService<B, H>>,
network_service: Swarm<B, H>,
import_queue: Box<dyn ImportQueue<B>>,
from_worker: mpsc::UnboundedReceiver<ServiceToWorkerMsg<B, H>>,
light_client_rqs: Option<mpsc::UnboundedReceiver<RequestData<B>>>,
event_streams: Vec<mpsc::UnboundedSender<Event>>,
metrics: Option<Metrics>,
}
struct Metrics {
connections: Gauge<U64>,
import_queue_blocks_submitted: Counter<U64>,
import_queue_finality_proofs_submitted: Counter<U64>,
import_queue_justifications_submitted: Counter<U64>,
is_major_syncing: Gauge<U64>,
kbuckets_num_nodes: Gauge<U64>,
network_per_sec_bytes: GaugeVec<U64>,
notifications_total: CounterVec<U64>,
num_event_stream_channels: Gauge<U64>,
opened_notification_streams: GaugeVec<U64>,
peers_count: Gauge<U64>,
peerset_num_discovered: Gauge<U64>,
peerset_num_requested: Gauge<U64>,
random_kademalia_queries_total: Counter<U64>,
}
impl Metrics {
fn register(registry: &Registry) -> Result<Self, PrometheusError> {
Ok(Self {
connections: register(Gauge::new(
"sub_libp2p_connections", "Number of libp2p connections"
)?, registry)?,
import_queue_blocks_submitted: register(Counter::new(
"import_queue_blocks_submitted",
"Number of blocks submitted to the import queue.",
)?, registry)?,
import_queue_finality_proofs_submitted: register(Counter::new(
"import_queue_finality_proofs_submitted",
"Number of finality proofs submitted to the import queue.",
)?, registry)?,
import_queue_justifications_submitted: register(Counter::new(
"import_queue_justifications_submitted",
"Number of justifications submitted to the import queue.",
)?, registry)?,
is_major_syncing: register(Gauge::new(
"sub_libp2p_is_major_syncing", "Whether the node is performing a major sync or not.",
)?, registry)?,
kbuckets_num_nodes: register(Gauge::new(
"sub_libp2p_kbuckets_num_nodes", "Number of nodes in the Kademlia k-buckets"
)?, registry)?,
network_per_sec_bytes: register(GaugeVec::new(
Opts::new(
"sub_libp2p_network_per_sec_bytes",
"Average bandwidth usage per second"
),
&["direction"]
)?, registry)?,
notifications_total: register(CounterVec::new(
Opts::new(
"sub_libp2p_notifications_total",
"Number of notification received from all nodes"
),
&["direction", "protocol"]
)?, registry)?,
num_event_stream_channels: register(Gauge::new(
"sub_libp2p_num_event_stream_channels",
"Number of internal active channels that broadcast network events",
)?, registry)?,
opened_notification_streams: register(GaugeVec::new(
Opts::new(
"sub_libp2p_opened_notification_streams",
"Number of open notification substreams"
),
&["protocol"]
)?, registry)?,
peers_count: register(Gauge::new(
"sub_libp2p_peers_count", "Number of network gossip peers",
)?, registry)?,
peerset_num_discovered: register(Gauge::new(
"sub_libp2p_peerset_num_discovered", "Number of nodes stored in the peerset manager",
)?, registry)?,
peerset_num_requested: register(Gauge::new(
"sub_libp2p_peerset_num_requested", "Number of nodes that the peerset manager wants us to be connected to",
)?, registry)?,
random_kademalia_queries_total: register(Counter::new(
"sub_libp2p_random_kademalia_queries_total", "Number of random Kademlia queries started",
)?, registry)?,
})
}
fn update_with_network_event(&self, event: &Event) {
match event {
Event::NotificationStreamOpened { engine_id, .. } => {
self.opened_notification_streams.with_label_values(&[&engine_id_to_string(&engine_id)]).inc();
},
Event::NotificationStreamClosed { engine_id, .. } => {
self.opened_notification_streams.with_label_values(&[&engine_id_to_string(&engine_id)]).dec();
},
Event::NotificationsReceived { messages, .. } => {
for (engine_id, _) in messages {
self.notifications_total.with_label_values(&["in", &engine_id_to_string(&engine_id)]).inc();
}
},
_ => {}
}
}
}
impl<B: BlockT + 'static, H: ExHashT> Future for NetworkWorker<B, H> {
type Output = Result<(), io::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context) -> Poll<Self::Output> {
let this = &mut *self;
this.import_queue.poll_actions(cx, &mut NetworkLink {
protocol: &mut this.network_service,
});
if let Some(light_client_rqs) = this.light_client_rqs.as_mut() {
while let Poll::Ready(Some(rq)) = light_client_rqs.poll_next_unpin(cx) {
this.network_service.user_protocol_mut().add_light_client_request(rq);
}
}
loop {
let msg = match this.from_worker.poll_next_unpin(cx) {
Poll::Ready(Some(msg)) => msg,
Poll::Ready(None) => return Poll::Ready(Ok(())),
Poll::Pending => break,
};
match msg {
ServiceToWorkerMsg::AnnounceBlock(hash, data) =>
this.network_service.user_protocol_mut().announce_block(hash, data),
ServiceToWorkerMsg::RequestJustification(hash, number) =>
this.network_service.user_protocol_mut().request_justification(&hash, number),
ServiceToWorkerMsg::PropagateExtrinsic(hash) =>
this.network_service.user_protocol_mut().propagate_extrinsic(&hash),
ServiceToWorkerMsg::PropagateExtrinsics =>
this.network_service.user_protocol_mut().propagate_extrinsics(),
ServiceToWorkerMsg::GetValue(key) =>
this.network_service.get_value(&key),
ServiceToWorkerMsg::PutValue(key, value) =>
this.network_service.put_value(key, value),
ServiceToWorkerMsg::AddKnownAddress(peer_id, addr) =>
this.network_service.add_known_address(peer_id, addr),
ServiceToWorkerMsg::SyncFork(peer_ids, hash, number) =>
this.network_service.user_protocol_mut().set_sync_fork_request(peer_ids, &hash, number),
ServiceToWorkerMsg::EventStream(sender) =>
this.event_streams.push(sender),
ServiceToWorkerMsg::WriteNotification { message, engine_id, target } => {
if let Some(metrics) = this.metrics.as_ref() {
metrics.notifications_total.with_label_values(&["out", &engine_id_to_string(&engine_id)]).inc();
}
this.network_service.user_protocol_mut().write_notification(target, engine_id, message)
},
ServiceToWorkerMsg::RegisterNotifProtocol { engine_id, protocol_name } => {
let events = this.network_service.user_protocol_mut()
.register_notifications_protocol(engine_id, protocol_name);
for event in events {
this.event_streams.retain(|sender| sender.unbounded_send(event.clone()).is_ok());
}
},
ServiceToWorkerMsg::DisconnectPeer(who) =>
this.network_service.user_protocol_mut().disconnect_peer(&who),
}
}
loop {
let next_event = this.network_service.next_event();
futures::pin_mut!(next_event);
let poll_value = next_event.poll_unpin(cx);
match poll_value {
Poll::Pending => break,
Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::BlockImport(origin, blocks))) => {
if let Some(metrics) = this.metrics.as_ref() {
metrics.import_queue_blocks_submitted.inc();
}
this.import_queue.import_blocks(origin, blocks);
},
Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::JustificationImport(origin, hash, nb, justification))) => {
if let Some(metrics) = this.metrics.as_ref() {
metrics.import_queue_justifications_submitted.inc();
}
this.import_queue.import_justification(origin, hash, nb, justification);
},
Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::FinalityProofImport(origin, hash, nb, proof))) => {
if let Some(metrics) = this.metrics.as_ref() {
metrics.import_queue_finality_proofs_submitted.inc();
}
this.import_queue.import_finality_proof(origin, hash, nb, proof);
},
Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::RandomKademliaStarted)) => {
if let Some(metrics) = this.metrics.as_ref() {
metrics.random_kademalia_queries_total.inc();
}
},
Poll::Ready(SwarmEvent::Behaviour(BehaviourOut::Event(ev))) => {
this.event_streams.retain(|sender| sender.unbounded_send(ev.clone()).is_ok());
if let Some(metrics) = this.metrics.as_ref() {
metrics.update_with_network_event(&ev);
}
},
Poll::Ready(SwarmEvent::Connected(peer_id)) => {
trace!(target: "sub-libp2p", "Libp2p => Connected({:?})", peer_id);
if let Some(metrics) = this.metrics.as_ref() {
metrics.connections.inc();
}
},
Poll::Ready(SwarmEvent::Disconnected(peer_id)) => {
trace!(target: "sub-libp2p", "Libp2p => Disconnected({:?})", peer_id);
if let Some(metrics) = this.metrics.as_ref() {
metrics.connections.dec();
}
},
Poll::Ready(SwarmEvent::NewListenAddr(addr)) =>
trace!(target: "sub-libp2p", "Libp2p => NewListenAddr({})", addr),
Poll::Ready(SwarmEvent::ExpiredListenAddr(addr)) =>
trace!(target: "sub-libp2p", "Libp2p => ExpiredListenAddr({})", addr),
Poll::Ready(SwarmEvent::UnreachableAddr { peer_id, address, error }) =>
trace!(target: "sub-libp2p", "Libp2p => Failed to reach {:?} through {:?}: {}", peer_id, address, error),
Poll::Ready(SwarmEvent::StartConnect(peer_id)) =>
trace!(target: "sub-libp2p", "Libp2p => StartConnect({:?})", peer_id),
};
}
let num_connected_peers = this.network_service.user_protocol_mut().num_connected_peers();
this.num_connected.store(num_connected_peers, Ordering::Relaxed);
{
let external_addresses = Swarm::<B, H>::external_addresses(&this.network_service).cloned().collect();
*this.external_addresses.lock() = external_addresses;
}
let is_major_syncing = match this.network_service.user_protocol_mut().sync_state() {
SyncState::Idle => false,
SyncState::Downloading => true,
};
this.is_major_syncing.store(is_major_syncing, Ordering::Relaxed);
if let Some(metrics) = this.metrics.as_ref() {
metrics.network_per_sec_bytes.with_label_values(&["in"]).set(this.service.bandwidth.average_download_per_sec());
metrics.network_per_sec_bytes.with_label_values(&["out"]).set(this.service.bandwidth.average_upload_per_sec());
metrics.is_major_syncing.set(is_major_syncing as u64);
metrics.kbuckets_num_nodes.set(this.network_service.num_kbuckets_entries() as u64);
metrics.num_event_stream_channels.set(this.event_streams.len() as u64);
metrics.peers_count.set(num_connected_peers as u64);
metrics.peerset_num_discovered.set(this.network_service.user_protocol().num_discovered_peers() as u64);
metrics.peerset_num_requested.set(this.network_service.user_protocol().requested_peers().count() as u64);
}
Poll::Pending
}
}
impl<B: BlockT + 'static, H: ExHashT> Unpin for NetworkWorker<B, H> {
}
fn engine_id_to_string(id: &ConsensusEngineId) -> Cow<str> {
if let Ok(s) = std::str::from_utf8(&id[..]) {
Cow::Borrowed(s)
} else {
Cow::Owned(format!("{:?}", id))
}
}
type Swarm<B, H> = libp2p::swarm::Swarm<Behaviour<B, H>>;
struct NetworkLink<'a, B: BlockT, H: ExHashT> {
protocol: &'a mut Swarm<B, H>,
}
impl<'a, B: BlockT, H: ExHashT> Link<B> for NetworkLink<'a, B, H> {
fn blocks_processed(
&mut self,
imported: usize,
count: usize,
results: Vec<(Result<BlockImportResult<NumberFor<B>>, BlockImportError>, B::Hash)>
) {
self.protocol.user_protocol_mut().blocks_processed(imported, count, results)
}
fn justification_imported(&mut self, who: PeerId, hash: &B::Hash, number: NumberFor<B>, success: bool) {
self.protocol.user_protocol_mut().justification_import_result(hash.clone(), number, success);
if !success {
info!("Invalid justification provided by {} for #{}", who, hash);
self.protocol.user_protocol_mut().disconnect_peer(&who);
self.protocol.user_protocol_mut().report_peer(who, ReputationChange::new_fatal("Invalid justification"));
}
}
fn request_justification(&mut self, hash: &B::Hash, number: NumberFor<B>) {
self.protocol.user_protocol_mut().request_justification(hash, number)
}
fn request_finality_proof(&mut self, hash: &B::Hash, number: NumberFor<B>) {
self.protocol.user_protocol_mut().request_finality_proof(hash, number)
}
fn finality_proof_imported(
&mut self,
who: PeerId,
request_block: (B::Hash, NumberFor<B>),
finalization_result: Result<(B::Hash, NumberFor<B>), ()>,
) {
let success = finalization_result.is_ok();
self.protocol.user_protocol_mut().finality_proof_import_result(request_block, finalization_result);
if !success {
info!("Invalid finality proof provided by {} for #{}", who, request_block.0);
self.protocol.user_protocol_mut().disconnect_peer(&who);
self.protocol.user_protocol_mut().report_peer(who, ReputationChange::new_fatal("Invalid finality proof"));
}
}
}